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

React Native中使用useAsyncStorage引发自定义抽屉组件无限渲染循环如何解决

无限渲染问题排查与修复方案

核心问题根源

两个核心逻辑错误叠加形成了渲染死循环,CustomDrawer 组件本身的写法没有问题,问题全部出在 App 根组件的实现上:

  • useEffect 缺失依赖数组,触发自循环
    当前 App 组件内的 useEffect 没有传入第二个参数(依赖数组),按照 React 规则,这种写法会在每一次组件渲染完成后都执行回调。回调内的 setTimeout 会调用 setActiveDrawer 修改组件状态,状态变更又会触发 App 重渲染,渲染完成后再次执行 useEffect,形成第一个循环闭环。
  • 渲染流程内直接调用 setState,触发二次循环
    你把 UpdateScreen 作为 Drawer 的 drawerContent 渲染内容,这个函数在每次侧边栏渲染时,都会执行 setNavigation(props.navigation)(即修改 Scren_navigation 状态)。React 明确要求渲染流程必须保持纯性,不能在渲染过程中修改组件状态:这里的 setState 会直接触发 App 重渲染,重渲染又会让 drawerContent 重新执行,再次调用 setState,和上面的 useEffect 循环形成叠加,彻底锁死页面。
  • 额外隐患:setTimeout 无清理机制
    没有在 useEffect 清理逻辑中清除已创建的定时器,会导致多次触发状态更新,加重渲染循环的问题。

分步修复实现

  1. 修正 setState 触发时机:所有状态更新都放到 useEffect 回调中执行,杜绝在渲染流程中直接调用 setState,保证渲染逻辑的纯性。
  2. 给 useEffect 补全依赖逻辑与定时器清理:按照实际业务逻辑指定依赖项,避免无意义的重复执行,同时在 effect 清理函数中清除未执行的定时器。
  3. 优化 activeDrawer 的状态存储逻辑:不再将 JSX 元素直接存到 state 中,改为存储对应的抽屉组件标识,在渲染时根据标识返回对应组件,减少不必要的元素重建。

修复后的 App 组件参考代码:

import {useEffect, useRef, useState} from 'react';
import {NavigationContainer} from '@react-navigation/native';
import {createDrawerNavigator} from '@react-navigation/drawer';
import CustomDrawer from './CustomDrawer';
import NoInternetDrawer from './NoInternetDrawer';
import EmptyDrawer from './EmptyDrawer';
import useNetInfo from './useNetInfo'; // 原有网络检测 Hook
import Navigation from './Navigation';

const Drawer = createDrawerNavigator();

const App = () => {
  const network = useNetInfo();
  // 用 ref 暂存导航对象,避免渲染流程中直接触发状态更新
  const navRef = useRef(null);
  // 存储有效导航对象
  const [navigation, setNavigation] = useState(null);
  // 存储当前需要展示的抽屉类型,替代直接存 JSX 的写法
  const [drawerType, setDrawerType] = useState('empty');

  // 监听 ref 变化,在 effect 中完成导航对象的状态更新(不在渲染流程中执行 setState)
  useEffect(() => {
    if (navRef.current && navRef.current !== navigation) {
      setNavigation(navRef.current);
    }
  }, [navRef.current, navigation]);

  // 监听网络状态与导航有效性,切换对应抽屉组件
  useEffect(() => {
    // 未获取到有效导航对象时不执行后续逻辑
    if (!navigation) return;
    const timer = setTimeout(() => {
      setDrawerType(network.isInternetReachable ? 'custom' : 'noInternet');
    }, 5000);
    // 清理未执行的定时器,避免重复触发
    return () => clearTimeout(timer);
  }, [network.isInternetReachable, navigation]);

  // 根据存储的抽屉类型渲染对应内容
  const renderDrawerContent = () => {
    switch (drawerType) {
      case 'custom':
        return <CustomDrawer navigation={navigation} />;
      case 'noInternet':
        return <NoInternetDrawer navigation={navigation} />;
      default:
        return <EmptyDrawer />;
    }
  };

  return (
    <NavigationContainer>
      <Drawer.Navigator
        drawerContent={props => {
          // 仅将导航对象存入 ref,不直接调用 setState
          navRef.current = props.navigation;
          return renderDrawerContent();
        }}>
        <Drawer.Screen name="StackNavigation" component={Navigation} />
      </Drawer.Navigator>
    </NavigationContainer>
  );
};

export default App;

补充说明

CustomDrawer 组件内的逻辑本身没有问题:

  • 内部的 useEffect 传了空依赖数组,只会在组件挂载时执行一次 token 读取逻辑,不会触发重复渲染
  • setLoggedIn 的调用是在异步回调内执行,不在渲染流程中,符合 React 的状态更新规则

内容的提问来源于stack exchange,提问作者ZeevhY Org.

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.10.25 05:27:10