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

React Native实现动态侧边抽屉菜单,解决状态更新导致的无限循环问题

问题根因
  • 你在StackNavigationManager组件内部定义了DrawerScreen和CustomDrawerContent两个子组件,每次父组件状态更新触发重渲染时,这两个子组件都会被重新创建为全新的函数,React会判定为组件类型变更,直接销毁旧实例挂载新实例。新挂载的Home组件会再次执行空依赖的useEffect,调用validateMenuData更新menuData状态,再次触发StackNavigationManager重渲染,形成死循环。
  • authContext的useMemo没有传入第二个依赖数组参数,导致每次StackNavigationManager渲染都会生成全新的authContext对象,触发所有消费该Context的组件额外重渲染,加剧了循环问题。
  • 小错误:React组件名必须首字母大写,你原代码的home组件名首字母小写,也可能导致额外的渲染异常。
解决方案
  1. 将所有嵌套定义的子组件迁移到StackNavigationManager外部,避免重复创建组件实例
  2. 给useMemo补充空依赖数组,保证authContext只初始化一次
  3. 把menuData也加入Context中,方便抽离到外部的CustomDrawerContent访问菜单数据

修正后代码示例

StackNavigationManager.js

import React from 'react';
import { NavigationContainer } from '@react-navigation/native';
import { createStackNavigator } from '@react-navigation/stack';
import { createDrawerNavigator, DrawerItemList, DrawerItem } from '@react-navigation/drawer';
import { SafeAreaView, FlatList } from 'react-native';
import { AuthContext } from './context';
import Home from './Home';

const Drawer = createDrawerNavigator();
const Stack = createStackNavigator();

// 抽离到外部的抽屉内容组件
function CustomDrawerContent(props) {
  const { menuData } = React.useContext(AuthContext);
  return (
    <SafeAreaView style={{flex: 1}}>
      <DrawerItemList {...props} />
      <FlatList
        data={menuData}
        keyExtractor={data => data.id.toString()}
        renderItem={({item}) => {
          return (
            <DrawerItem
              label={item.name}
              onPress={() => alert(item.id)}
            />
          );
        }}
      />
    </SafeAreaView>
  );
}

// 抽离到外部的抽屉路由组件
const DrawerScreen = ({route, navigation}) => (
  <Drawer.Navigator
    drawerContent={props => <CustomDrawerContent {...props} />}
  >
    <Drawer.Screen
      name="HomeDrawer"
      component={Home}
    />
  </Drawer.Navigator>
);

const StackNavigationManager = () => {
  const [menuData, setMenuData] = React.useState([]);

  // 补充空依赖数组,setMenuData为React保证的稳态值,不需要加入依赖
  const authContext = React.useMemo(() => ({
    menuData,
    validateMenuData: (newMenuData) => {
      setMenuData(newMenuData);
    },
  }), []);

  return (
    <AuthContext.Provider value={authContext}>
      <NavigationContainer>
        <Stack.Navigator
          initialRouteName="Home"
        >
          <Stack.Screen
            name="Home"
            component={DrawerScreen}
            options={{
              headerShown: false,
            }}
          />
        </Stack.Navigator>
      </NavigationContainer>
    </AuthContext.Provider>
  );
};

export default StackNavigationManager;

Home.js

仅需要修正组件名首字母大写即可,原有逻辑不需要改动,切换子系统时直接调用validateMenuData即可同步更新抽屉菜单。

import {AuthContext} from './context';

// 组件名首字母必须大写
const Home = ({navigation}) => {
  const {validateMenuData} = React.useContext(AuthContext);

  React.useEffect(() => {
    console.log('MOUNTED EFFECT');
    getSubSystems();

    return () => {
      isMounted = false;
      console.log('UNMOUNTED EFFECT');
    };
  }, []);

  const getSubSystems = async () => {
    const subSystemModel = {
      isActive: true,
      isVisible: true,
    };
    fetchSubSystemData(
      subSystemModel,
      response => {
        if (response.list !== null) {
          let listData = [...response.list];
          
          fetchMenuAction(
            listData[0].id,
            response2 => {
              validateMenuData([...response2.list]);
            },
          );
        }
      },
      () => {},
    );
  };

  // 原有页面渲染逻辑
}

export default Home;

内容的提问来源于stack exchange,提问作者H.A

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.30 20:15:05