React useState如何避免组件加载时默认值与localStorage值切换的闪烁问题
解决方案
方案1:直接在useState初始化时读取localStorage(客户端渲染场景首选)
闪烁的根本原因是useEffect会在组件首次渲染完成后才执行,因此首次渲染必然使用默认值,等state更新后触发二次渲染才会切换为正确值。
直接把读取localStorage的逻辑放到useState的惰性初始化函数里即可,这个函数只会在组件首次初始化时执行一次,首次渲染就能拿到正确的状态值:
// 直接用惰性初始化函数计算初始值,不需要再在useEffect里更新这两个state const [currentPage, setCurrentPage] = useState(() => { if (typeof window === "undefined") return 1; const savedPage = localStorage.getItem("currentPage"); return savedPage ? parseInt(savedPage, 10) : 1; }); const [calculatedOffset, setCalculatedOffset] = useState(() => { if (typeof window === "undefined") return 0; const savedOffset = localStorage.getItem("calculatedOffset"); return savedOffset ? parseInt(savedOffset, 10) : 0; });
用这个方案可以直接删掉原来读取localStorage的useEffect,首次渲染就会使用正确的分页参数,完全不会出现闪烁。
方案2:加水合状态兼容SSR场景
如果你用的是Next.js等SSR/SSG框架,服务端运行时没有window对象,无法在初始化state时读取localStorage,可以加一个加载状态控制渲染时机:
const [isHydrated, setIsHydrated] = useState(false); const [currentPage, setCurrentPage] = useState(1); const [calculatedOffset, setCalculatedOffset] = useState(0); useEffect(() => { const integerPage = parseInt(localStorage.getItem("currentPage") || "1", 10); setCurrentPage(integerPage); const integerOffset = parseInt(localStorage.getItem("calculatedOffset") || "0", 10); setCalculatedOffset(integerOffset); // 状态更新完成后标记为已水合 setIsHydrated(true); }, []); // 未完成水合前渲染加载占位,避免显示错误的默认值内容 if (!isHydrated) { return <div>加载中...</div>; // 也可以渲染分页列表的骨架屏,体验更好 } // 后面是正常的页面渲染逻辑
这个方案会等状态读取并更新完成后才渲染实际内容,也能避免闪烁。
额外注意点
parseInt建议传入第二个参数10指定十进制解析,避免部分场景下非十进制字符串解析错误- 如果分页参数会频繁更新,建议在更新state的同时写入localStorage,保证下次读取的是最新值
内容的提问来源于stack exchange,提问作者Dev
相关产品推荐
相关产品推荐

