前言

我的App应用程序上须要一个抽屉菜单。React navigation drawer导航反对此性能,然而扭转了屏幕的构造,我不心愿更改,因为我只是其中一个屏幕上须要用到这个简略抽屉菜单组件。大抵的成果如下:

装置依赖

react-native-modal 组件大抵能够满足我的需要,模态框加上左右移入移出的动画加上手势根本能实现侧拉抽屉的组件。当初装置它:

yarn add react-native-modal -save

编写SideMenu.js

SideMenu组件是侧拉菜单外面展现的内容

import React from 'react';import { Text, View, SafeAreaView } from 'react-native';import styles from './styles';const Title = ({ title }) => {  return <Text style={styles.title}>{title}</Text>;};const SwitchText = ({ text }) => {  return <Text style={styles.switchText}>{text}</Text>;};const Description = ({ text }) => {  return <Text style={styles.description}>{text}</Text>;};const SideMenu = props => {  return (    <SafeAreaView style={styles.safeAreaView}>      <View style={styles.container}>        <Title title="Timeline" />        <View>          <View style={styles.swithBlock}>            <SwitchText text="Ratings with reviews only" />          </View>          <Description text="When enabled, on your timeline we will only show ratings with reviews." />        </View>      </View>      <View style={styles.footer}>        <Text style={styles.link}>Press to call parent function</Text>      </View>    </SafeAreaView>  );};export default SideMenu;
import { StyleSheet } from 'react-native';import { screenSize } from '../../../utils/tools';const styles = StyleSheet.create({  safeAreaView: {    flex: 1,    backgroundColor: '#fff'  },  container: {    margin: 12,    flex: 1  },  title: {    marginTop: 15,    marginBottom: 10,    color: '#444',    fontSize: 14  },  swithBlock: {    flexDirection: 'row',    justifyContent: 'space-between',    alignItems: 'center'  },  switchText: {    fontSize: 14,    color: '#222'  },  link: {    padding: 5,    color: '#892853'  },  description: {    fontSize: 13,    color: '#555',    marginTop: 12,    marginBottom: 6  }});export default styles;

编写主页面

援用组件,通过isVisible参数管制菜单显示暗藏,toggleSideMenu 办法管制切换显示暗藏,还有一些管制入场动画的参数。我为了使它更靠近抽屉组件,所以应用slideInLeft

import React, { useState } from 'react';import { Text, TouchableOpacity, View } from 'react-native';import styles from './styles';import { ButtonGroup, Header } from 'react-native-elements';import common from '../../styles/common';import Modal from 'react-native-modal';import SideMenu from './SideMenu';import Ionicons from 'react-native-vector-icons/Ionicons';import { useNavigation } from '@react-navigation/native';const ProjectDetail = props => {  const { route } = props;  console.log('路由参数', route.params);  const [visible, setVisible] = useState(false);  const [selectedIndex, setSelectedIndex] = useState(0);  const navigation = useNavigation();  const toggleSideMenu = () => {    setVisible(!visible);  };  const updateIndex = index => {    setSelectedIndex(index);  };  const component1 = () => <Text>文件</Text>;  const component2 = () => <Text>流程核心</Text>;  const buttons = [{ element: component1 }, { element: component2 }];  return (    <View style={common.container}>      <Header        leftComponent={          <View>            <TouchableOpacity onPress={navigation.goBack}>              <Ionicons name="arrow-back" size={24} color={'#fff'} />            </TouchableOpacity>          </View>        }        centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}        rightComponent={          <View>            <TouchableOpacity onPress={toggleSideMenu}>              <Ionicons name="menu" size={24} color={'#fff'} />            </TouchableOpacity>          </View>        }      />      <ButtonGroup        onPress={updateIndex}        selectedIndex={selectedIndex}        buttons={buttons}        containerStyle={{ height: 28 }}      />      <Modal        isVisible={visible}        onBackdropPress={toggleSideMenu} // Android back press        onSwipeComplete={toggleSideMenu} // Swipe to discard        animationIn="slideInLeft" // Has others, we want slide in from the left        animationOut="slideOutLeft" // When discarding the drawer        swipeDirection="left" // Discard the drawer with swipe to left        useNativeDriver // Faster animation        hideModalContentWhileAnimating // Better performance, try with/without        propagateSwipe // Allows swipe events to propagate to children components (eg a ScrollView inside a modal)        style={styles.sideMenuStyle}      >        <SideMenu />      </Modal>    </View>  );};export default ProjectDetail;
import { StyleSheet } from 'react-native';import { screenSize } from '../../utils/tools';const styles = StyleSheet.create({  sideMenuStyle: {    width: screenSize.width * 0.75,    margin: 0  }});export default styles;

封装

因为其余时候有可能局部页面也须要相似的抽屉组件,所以把这个Modal组件封装一下,首先想到咱们须要在内部(有可能是在页面头部菜单)点击登程显示菜单的性能,所以必须裸露给父组件调用子组件外部办法,那么咱们就须要forwardRefuseImperativeHandle:
forwardRef:援用父组件的ref实例,成为子组件的一个参数,能够援用父组件的ref绑定到子组件本身的节点上.
useImperativeHandle : 第一个参数,接管一个通过forwardRef援用父组件的ref实例,第二个参数一个回调函数,返回一个对象,对象外面存储须要裸露给父组件的属性或办法;
官网倡议useImperativeHandleforwardRef同时应用,缩小裸露给父组件的属性,防止应用 ref 这样的命令式代码。
失常状况下 ref 是不能挂在到函数组件上的,因为函数组件没有实例,然而 useImperativeHandle 为咱们提供了一个相似实例的货色。它帮忙咱们通过 useImperativeHandle 的第 2 个参数,所返回的对象的内容挂载到 父组件的 ref.current 上。
forwardRef会创立一个React组件,这个组件可能将其承受的 ref 属性转发到其组件树下的另一个组件中。
封装Drawer后,如下:

import React, { useState, useImperativeHandle } from 'react';import styles from './styles';import Modal from 'react-native-modal';const Drawer = React.forwardRef((props, ref) => {  const [visible, setVisible] = useState(false);  const toggleSideMenu = () => {    setVisible(!visible);  };  useImperativeHandle(ref, () => ({    toggleSideMenu: () => toggleSideMenu()  }));  return (    <Modal      isVisible={visible}      onBackdropPress={toggleSideMenu} // Android back press      onSwipeComplete={toggleSideMenu} // Swipe to discard      animationIn="slideInLeft" // Has others, we want slide in from the left      animationOut="slideOutLeft" // When discarding the drawer      swipeDirection="left" // Discard the drawer with swipe to left      useNativeDriver // Faster animation      hideModalContentWhileAnimating // Better performance, try with/without      propagateSwipe // Allows swipe events to propagate to children components (eg a ScrollView inside a modal)      style={styles.sideMenuStyle}    >      {props.children}    </Modal>  );});export default Drawer;

父组件应用:

import React, { useRef, useState } from 'react';import { Text, TouchableOpacity, View } from 'react-native';import styles from './styles';import { ButtonGroup, Header } from 'react-native-elements';import common from '../../styles/common';import SideMenu from './SideMenu';import Ionicons from 'react-native-vector-icons/Ionicons';import { useNavigation } from '@react-navigation/native';import Drawer from '../../components/Drawer';const ProjectDetail = props => {  const { route } = props;  console.log('路由参数', route.params);  const [selectedIndex, setSelectedIndex] = useState(0);  const navigation = useNavigation();  const drawerRef = useRef();  const updateIndex = index => {    setSelectedIndex(index);  };  const component1 = () => <Text>文件</Text>;  const component2 = () => <Text>流程核心</Text>;  const buttons = [{ element: component1 }, { element: component2 }];  return (    <View style={common.container}>      <Header        leftComponent={          <View>            <TouchableOpacity onPress={navigation.goBack}>              <Ionicons name="arrow-back" size={24} color={'#fff'} />            </TouchableOpacity>          </View>        }        centerComponent={{ text: 'MY TITLE', style: { color: '#fff' } }}        rightComponent={          <View>            <TouchableOpacity              onPress={() => drawerRef.current.toggleSideMenu()}            >              <Ionicons name="menu" size={24} color={'#fff'} />            </TouchableOpacity>          </View>        }      />      <ButtonGroup        onPress={updateIndex}        selectedIndex={selectedIndex}        buttons={buttons}        containerStyle={{ height: 28 }}      />      <Drawer ref={drawerRef}>        <SideMenu />      </Drawer>    </View>  );};export default ProjectDetail;