React可复用组件中如何缓存API数据实现跨实例复用
问题解决实现方案
核心思路是利用ES模块单例的特性,在组件所在模块的顶层声明缓存变量,变量不会随组件卸载销毁,全应用所有复用该组件的位置共享同一份缓存,全程不需要引入第三方依赖。
实现步骤
- 在
SelectionBox组件的模块顶层声明两个缓存变量,分别存储已加载完成的选项数据、加载中的请求Promise,既避免组件卸载后数据丢失,也能防止多实例同时挂载时的重复请求竞态问题 - 将接口调用逻辑放入
useEffect钩子中,满足「组件挂载阶段调用接口」的要求 - 组件挂载时优先判断缓存状态:已有缓存直接使用、存在进行中的请求则等待请求返回、无缓存也无进行中请求时才发起接口调用
- 同步修复原代码中存在的基础语法bug
完整代码实现
修正后的SelectionBox组件
import { useState, useEffect } from 'react'; import service from '你的接口服务路径'; import Select from '你的Select组件路径'; // 模块顶层缓存,ES模块单例特性保证全应用共用,组件卸载不销毁 let cachedOptions = null; let loadingPromise = null; function SelectionBox() { const [value, setValue] = useState(""); // 初始化时如果已有缓存直接用,避免首屏空选项闪烁 const [options, setOptions] = useState(cachedOptions || []); useEffect(() => { // 已有缓存直接终止逻辑,不发请求 if (cachedOptions) return; // 已有正在进行的请求,等待请求完成后更新状态即可,不重复发起请求 if (loadingPromise) { loadingPromise.then(opt => { setOptions(opt); }).catch(err => { console.log(err); }); return; } // 首次无缓存、无进行中请求,发起接口调用,将Promise存入缓存 loadingPromise = service.getData1() .then(data => service.getData2(data)) .then(opt => { cachedOptions = opt; setOptions(opt); return opt; }) .catch(err => { // 请求失败时清空Promise缓存,允许后续组件挂载时重试 loadingPromise = null; console.log(err); }); }, []); return ( <Select label="select" options={options} value={value} onChange={(val) => setValue(val)} /> ); } export default SelectionBox;
原代码基础bug修正
原App组件中按钮点击事件写法错误,会导致组件渲染时直接触发弹窗打开,修正后代码:
import { useState } from 'react'; import Dialog from './Dialog'; function App() { const [toggle, setToggle] = useState(false); return ( <> <button onClick={() => setToggle(true)}>Show Dialog</button> {toggle && <Dialog onClose={() => setToggle(false)} />} </> ); } export default App;
记得在Dialog组件中接收onClose参数,绑定弹窗关闭按钮的触发逻辑即可。
方案符合要求验证
- 无任何第三方依赖,仅使用React原生Hook和JS语言特性实现
- 接口仅在SelectionBox首次挂载时触发一次,后续弹窗反复开关导致组件卸载重挂载、或其他页面复用该组件时,均直接读取缓存数据,不会重复发起请求
- 接口调用逻辑放在
useEffect中,在组件挂载完成后执行,符合挂载阶段调用的要求 - 额外兼容了多实例同时挂载的竞态问题、请求失败后的重试逻辑,初始化时直接读取缓存还能避免选项空状态闪烁
内容的提问来源于stack exchange,提问作者Rishi Mishra
相关产品推荐
相关产品推荐

