react native drawer

前言

個人App應用程序上須要一個抽屜菜單。React navigation drawer導航支持此功能,可是改變了屏幕的結構,我不但願更改,由於我只是其中一個屏幕上須要用到這個簡單抽屜菜單組件。大體的效果以下:
drawer.gifjavascript

安裝依賴

react-native-modal 組件大體能夠知足個人需求,模態框加上左右移入移出的動畫加上手勢基本能實現側拉抽屜的組件。如今安裝它:java

yarn add react-native-modal -save

編寫SideMenu.js

SideMenu組件是側拉菜單裏面展現的內容react

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 react-native

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;

父組件使用:ide

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;
相關文章
相關標籤/搜索