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

React:等待API请求与LocalStorage设置完成后再渲染子组件

问题解决:首次加载时LocalStorage数据未就绪导致子组件渲染异常

问题场景

入口组件通过异步请求获取接口数据,处理后存入LocalStorage;子组件依赖LocalStorage中的数据渲染统计内容。但首次打开应用时,子组件渲染早于LocalStorage写入完成,导致数据为空,只有刷新页面(LocalStorage已有缓存)后才能正常显示统计数量。

解决方案

方法一:状态提升(推荐,符合React设计模式)

将接口获取的数据先存入入口组件的状态,同时写入LocalStorage做持久化备份,再通过props或Context传递给子组件,避免子组件依赖LocalStorage的异步写入时机。

入口组件修改:

const App = () => {
  const [appData, setAppData] = React.useState<{libraries?: unknown[], softwareComponents?: unknown[]}>({});
  const [isLoading, setIsLoading] = React.useState(true);

  React.useEffect(() => {
    const fetchData = async () => {
      try {
        const libraries: unknown[] = await sendRequest('/libraries');
        const softwareComponents: unknown[] = await sendRequest('/softwareComponents');
        
        const processedLibraries = arraySetup(libraries, 'libraries');
        const processedSoftware = arraySetup(softwareComponents, 'software-components');
        
        // 同时写入LocalStorage和组件状态
        localStorage.setItem('libraries', JSON.stringify(processedLibraries));
        localStorage.setItem('softwareComponents', JSON.stringify(processedSoftware));
        
        setAppData({
          libraries: processedLibraries,
          softwareComponents: processedSoftware
        });
      } catch (err) {
        console.error(err);
      } finally {
        setIsLoading(false);
      }
    };
    isAuthenticated() && fetchData();
  }, []);

  // 加载中显示占位内容
  if (isLoading) {
    return <div>加载中...</div>;
  }

  return (
    <div>
      <ChildComponent 
        libraries={appData.libraries} 
        softwareComponents={appData.softwareComponents} 
      />
    </div>
  );
};

子组件修改:

const ChildComponent = ({ libraries, softwareComponents }: {libraries?: unknown[], softwareComponents?: unknown[]}) => {
  const [data, setData] = React.useState<Array<any>>([]);

  React.useEffect(() => {
    // 监听props变化,数据就绪后更新状态
    if (libraries && softwareComponents) {
      setData([...libraries, ...softwareComponents]);
    }
  }, [libraries, softwareComponents]);

  const getDataLength = (category: string) => {
    return (data || []).filter((item: any) => item.category === category).length;
  };

  return (
    <React.Fragment>
      <OwcGrid item xs={12} s={4}>
        <LibrariesCard numberOfElements={getDataLength('libraries')} />
      </OwcGrid>
    </React.Fragment>
  );
};

方法二:子组件监听LocalStorage变化

通过轮询或自定义事件监听LocalStorage的写入状态,数据就绪后更新组件状态。

子组件修改:

const ChildComponent = () => {
  const [data, setData] = React.useState<Array<any>>([]);

  // 统一读取LocalStorage数据的逻辑
  const loadStorageData = () => {
    const libraries = getLocalStorageItem('libraries');
    const softwareComponents = getLocalStorageItem('softwareComponents');
    if (libraries && softwareComponents) {
      setData([...libraries, ...softwareComponents]);
      return true;
    }
    return false;
  };

  React.useEffect(() => {
    // 初始化读取一次
    const isReady = loadStorageData();
    
    // 未就绪则启动轮询,直到数据写入完成
    let interval: NodeJS.Timeout | undefined;
    if (!isReady) {
      interval = setInterval(() => {
        const ready = loadStorageData();
        if (ready) clearInterval(interval);
      }, 100); // 轮询间隔可根据实际调整
    }

    // 监听其他标签页的LocalStorage变化
    window.addEventListener('storage', loadStorageData);

    // 清理副作用
    return () => {
      window.removeEventListener('storage', loadStorageData);
      if (interval) clearInterval(interval);
    };
  }, []);

  const getDataLength = (category: string) => {
    return (data || []).filter((item: any) => item.category === category).length;
  };

  return (
    <React.Fragment>
      <OwcGrid item xs={12} s={4}>
        <LibrariesCard numberOfElements={getDataLength('libraries')} />
      </OwcGrid>
    </React.Fragment>
  );
};

方法三:入口组件控制子组件渲染时机

在入口组件中等待LocalStorage写入完成后,再渲染子组件。

入口组件修改:

const App = () => {
  const [isDataReady, setIsDataReady] = React.useState(false);

  React.useEffect(() => {
    const fetchData = async () => {
      try {
        const libraries: unknown[] = await sendRequest('/libraries');
        const softwareComponents: unknown[] = await sendRequest('/softwareComponents');
        
        localStorage.setItem('libraries', JSON.stringify(arraySetup(libraries, 'libraries')));
        localStorage.setItem('softwareComponents', JSON.stringify(arraySetup(softwareComponents, 'software-components')));
        
        // 数据写入完成后标记就绪
        setIsDataReady(true);
      } catch (err) {
        console.error(err);
        // 出错也标记就绪,避免页面一直处于加载状态
        setIsDataReady(true);
      }
    };
    isAuthenticated() && fetchData();
  }, []);

  if (!isDataReady) {
    return <div>加载中...</div>;
  }

  return (
    <div>
      <ChildComponent />
    </div>
  );
};

方案说明

  • 优先选择方法一:状态提升是React组件通信的标准方式,避免依赖LocalStorage的异步特性,同时保留LocalStorage的持久化能力,逻辑更清晰可靠。
  • 方法二适合无法修改父组件逻辑的场景,但轮询会带来轻微性能开销,需合理设置间隔时间。
  • 方法三简单直接,但会导致子组件延迟渲染,需配合友好的加载提示。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.17 08:15:43