484 字
1 分钟
React 为什么刷新页面,异步加载的本地缓存会被清空?
最近在做一个部署日志功能,需要把日志实时存入 IndexedDB,在开发中遇到了一个 Bug:明明数据已经存进了数据库,但只要一刷新页面,缓存数据就莫名其妙被抹除了。
1. 现象描述
逻辑很简单:
useState初始化一个log字符串。- 一个
useEffect监听log变化,只要有更新就save到本地数据库。 - 另一个
useEffect在组件挂载时,从数据库get出数据并setLog。
Bug:刷新页面,控制台数据库里曾有过数据,但瞬间变成了空字符串。
2. 为什么数据被抹除了?
问题出在 React 组件的生命周期和异步操作的时间差上:
- 第一步(初始化): 组件挂载,
log的初始状态是''(空字符串)。 - 第二步(执行监听逻辑): 监听
log的useEffect发现log发生了变化(初始化)。此时,从数据库读取旧数据的异步操作还没完成,这个 Effect 就拿着初始值''去执行了保存,把数据库里的旧数据覆盖了。 - 第三步(读取结束): 异步读取终于拿到了结果,但此时数据库里已经是刚才被覆盖掉的空字符串了。
3. 解决方法:给保存逻辑加状态锁
我们需要确保在数据从数据库恢复到 State 之前,严禁往数据库写回任何数据。
这里适合用 useRef 来做一个“初始化锁”。
修正后的逻辑:
const [log, setLog] = useState('');const inited = useRef(false); // 初始化锁
// 挂载时异步读取useEffect(() => { db.getCache().then(res => { setLog(res || ''); // 读取完成并赋值后,取消锁 (标记为已初始化) inited.current = true; });}, []);
// 监听变化并保存useEffect(() => { // 如果锁没取消 (还在初始化),直接跳过保存 if (!inited.current) return; saveToDB(log);}, [log]);4. 为什么用 useRef 而不是 useState?
ref.current的修改是立即生效的,而useState的更新是异步的。在处理生命周期边缘的逻辑时,同步的标记位更安全。- 修改
ref不会触发组件重新渲染。
分享
如果这篇文章对你有帮助,欢迎分享给更多人!
React 为什么刷新页面,异步加载的本地缓存会被清空?
http://mizuki.heycheems.top/posts/react_为什么刷新页面异步加载的本地缓存会被清空/ 部分信息可能已经过时
相关文章 智能推荐











