乐趣区

关于react-native:react-native-drawer

前言

我的 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;
退出移动版