React组件卸载时异步保存本地存储失效及自动配置页实现问题
修复配置页面自动加载/保存的问题
核心问题分析
- 严格模式异步执行干扰:React开发环境的严格模式会重复执行
useEffect的setup和cleaner函数,你的异步loadSettings还未完成就被cleaner打断,导致提前保存了初始空对象。 - 闭包捕获旧state:空依赖的
useEffect中,cleaner函数捕获的是组件首次渲染时的settings(空对象),后续state更新不会改变这个闭包内的值,因此卸载时始终保存初始状态。
解决方案
1. 用useRef保存最新状态引用
通过useRef同步最新的settings值,让cleaner函数能访问到实时的配置数据。
2. 标记组件挂载状态,过滤无效操作
在开发环境严格模式下,避免未完成的异步操作干扰,同时防止组件卸载后执行setState。
修改后的完整代码
import { useState, useEffect, useRef } from 'react'; const ConfigPage = () => { const [settings, setSettings] = useState<settingStruct>({}); const latestSettingsRef = useRef<settingStruct>({}); const isMountedRef = useRef(false); // 同步最新settings到ref,保证随时能拿到最新值 useEffect(() => { latestSettingsRef.current = settings; }, [settings]); const loadSettings = async () => { if (!isMountedRef.current) return; try { const savedSetting = await loadJsonFile<settingStruct>("settings.json", {}); console.log("load: ", savedSetting); setSettings(savedSetting!); } catch (err) { console.error("加载配置失败:", err); } }; const saveSettings = async () => { const currentSettings = latestSettingsRef.current; console.log("save: ", currentSettings); try { await saveJsonFile('settings.json', currentSettings); } catch (err) { console.error("保存配置失败:", err); } }; useEffect(() => { isMountedRef.current = true; let isStrictModeReRun = true; loadSettings(); return () => { isMountedRef.current = false; // 严格模式下首次cleaner是重复执行,跳过保存 if (!isStrictModeReRun) { saveSettings(); } isStrictModeReRun = false; }; }, []); useEffect(() => { console.log("changed: ", settings); }, [settings]); // 你的UI组件示例 return ( <div> <input value={settings.authKey || ''} onChange={(e) => setSettings({...settings, authKey: e.target.value})} /> <button onClick={saveSettings}>手动保存</button> </div> ); }; export default ConfigPage;
关键修改说明
latestSettingsRef:每次settings更新时同步到ref,彻底解决闭包捕获旧值的问题,确保卸载时保存的是最新配置。isMountedRef:标记组件挂载状态,避免组件卸载后执行setState引发警告。isStrictModeReRun:过滤严格模式下的首次无效cleaner执行,解决开发环境中初始值被错误保存的问题,生产环境不受影响。
内容的提问来源于stack exchange,提问作者jh Z
相关产品推荐
相关产品推荐

