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
相关产品推荐
相关产品推荐

