You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React Native:如何复用DrawerLayout而非每个页面重复添加?

当然不用每个页面都写DrawerLayoutAndroid!教你优雅复用+选中状态管理

嘿,作为React Native新手遇到这种重复代码的问题太正常了——每个页面都塞一遍DrawerLayoutAndroid不仅冗余,后期改个样式都要翻遍所有页面,完全没必要!我们可以把抽屉抽成可复用的自定义组件,还能轻松加上选中/未选中的状态管理,一步解决你的问题。

下面给你一步步拆解实现思路:

1. 先抽离自定义抽屉组件(带选中状态)

首先创建一个CustomDrawer.js,把抽屉的结构、样式、选中逻辑都封装在这里:

import React, { Component } from 'react';
import { View, Text, TouchableOpacity, DrawerLayoutAndroid, StyleSheet } from 'react-native';

export default class CustomDrawer extends Component {
  state = {
    selectedRoute: 'Screen1' // 默认选中第一个页面
  };

  // 更新选中状态并跳转页面
  handleMenuPress = (routeName) => {
    this.setState({ selectedRoute: routeName });
    this.props.navigation.navigate(routeName);
    this.props.closeDrawer(); // 关闭抽屉
  };

  // 监听页面跳转,同步选中状态
  componentDidMount() {
    this.unsubscribe = this.props.navigation.addListener('didFocus', () => {
      const currentRoute = this.props.navigation.state.routeName;
      this.setState({ selectedRoute: currentRoute });
    });
  }

  componentWillUnmount() {
    this.unsubscribe();
  }

  render() {
    const { selectedRoute } = this.state;
    const drawerContent = (
      <View style={styles.drawerContainer}>
        <TouchableOpacity
          style={[styles.menuItem, selectedRoute === 'Screen1' && styles.selectedItem]}
          onPress={() => this.handleMenuPress('Screen1')}
        >
          <Text style={[styles.menuText, selectedRoute === 'Screen1' && styles.selectedText]}>页面1</Text>
        </TouchableOpacity>
        <TouchableOpacity
          style={[styles.menuItem, selectedRoute === 'Screen2' && styles.selectedItem]}
          onPress={() => this.handleMenuPress('Screen2')}
        >
          <Text style={[styles.menuText, selectedRoute === 'Screen2' && styles.selectedText]}>页面2</Text>
        </TouchableOpacity>
        {/* 可扩展更多菜单项 */}
      </View>
    );

    return (
      <DrawerLayoutAndroid
        ref={this.props.drawerRef} // 暴露ref给父组件控制开闭
        drawerWidth={250}
        drawerPosition={DrawerLayoutAndroid.positions.Left}
        renderNavigationView={() => drawerContent}
      >
        {this.props.children} {/* 这里放StackNavigator的页面内容 */}
      </DrawerLayoutAndroid>
    );
  }
}

const styles = StyleSheet.create({
  drawerContainer: {
    flex: 1,
    paddingTop: 50,
    backgroundColor: '#f5f5f5'
  },
  menuItem: {
    padding: 15,
    borderBottomWidth: 1,
    borderBottomColor: '#ddd'
  },
  selectedItem: {
    backgroundColor: '#e0e0e0'
  },
  menuText: {
    fontSize: 16
  },
  selectedText: {
    color: '#2196F3',
    fontWeight: 'bold'
  }
});

2. 用容器组件包裹StackNavigator

接下来创建一个DrawerContainer.js,把你的StackNavigator作为自定义抽屉的子组件,这样所有页面都会共享同一个抽屉:

import React, { Component } from 'react';
import { createStackNavigator } from 'react-navigation-stack';
import { TouchableOpacity, Text } from 'react-native';
import CustomDrawer from './CustomDrawer';
import Screen1 from './Screen1';
import Screen2 from './Screen2';

// 创建StackNavigator
const AppStack = createStackNavigator({
  Screen1: { screen: Screen1 },
  Screen2: { screen: Screen2 }
});

export default class DrawerContainer extends Component {
  drawerRef = null;

  // 自定义头部,添加抽屉开关按钮
  static navigationOptions = ({ navigation }) => ({
    headerLeft: () => (
      <TouchableOpacity onPress={() => navigation.getParam('openDrawer')()}>
        <Text style={{ padding: 10, fontSize: 20 }}>☰</Text>
      </TouchableOpacity>
    )
  });

  componentDidMount() {
    // 把控制抽屉的方法传递给StackNavigator的页面
    this.props.navigation.setParams({
      openDrawer: this.openDrawer,
      closeDrawer: this.closeDrawer
    });
  }

  openDrawer = () => {
    this.drawerRef?.openDrawer();
  };

  closeDrawer = () => {
    this.drawerRef?.closeDrawer();
  };

  render() {
    return (
      <CustomDrawer
        drawerRef={(ref) => (this.drawerRef = ref)}
        navigation={this.props.navigation}
      >
        <AppStack navigation={this.props.navigation} />
      </CustomDrawer>
    );
  }
}

3. 修改你的Screen1.js(不用再写Drawer!)

现在你的Screen1里只需要专注页面逻辑,抽屉的事情交给自定义组件处理,比如你的onScreen1Click可以改成这样:

import React, { Component } from 'react';
import { View, TouchableOpacity, Text, StyleSheet } from 'react-native';

export default class Screen1 extends Component {
  static navigationOptions = {
    header: null // 不需要默认头部可以保留,或者用容器的自定义头部
  };

  onScreen1Click = () => {
    // 直接调用容器传递的closeDrawer方法
    this.props.navigation.getParam('closeDrawer')();
    // 跳转到其他页面
    this.props.navigation.navigate('Screen2');
  };

  render() {
    return (
      <View style={styles.container}>
        <Text style={styles.title}>这是页面1</Text>
        <TouchableOpacity onPress={this.onScreen1Click} style={styles.button}>
          <Text style={styles.buttonText}>点击跳转到页面2并关闭抽屉</Text>
        </TouchableOpacity>
      </View>
    );
  }
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    justifyContent: 'center',
    alignItems: 'center',
    backgroundColor: '#fff'
  },
  title: {
    fontSize: 20,
    marginBottom: 20
  },
  button: {
    padding: 10,
    backgroundColor: '#2196F3',
    borderRadius: 5
  },
  buttonText: {
    color: '#fff',
    fontSize: 16
  }
});

这样一来,你再也不用在每个页面里重复写DrawerLayoutAndroid了,所有抽屉相关的逻辑都集中在自定义组件里,维护和修改都超级方便!而且选中状态会自动和当前页面同步,用户体验也更流畅。

内容的提问来源于stack exchange,提问作者zephyr

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.25 08:30:13