useState/useReducer初始化函数必须为纯函数吗?React本地存储问题咨询
React Radio状态与LocalStorage同步问题解答
初始实现的问题分析
你的初始代码在StrictMode下失效,核心原因是:
- StrictMode在开发环境会触发组件两次挂载,模拟卸载再挂载的场景
- 初始代码中,
useState的初始值固定为1,第一次挂载时,useEffect读取LocalStorage并更新状态,但第二次挂载时,状态又回到初始值1,同时监听状态变化的useEffect会将1重新写入LocalStorage,覆盖之前读取的值 - 两次挂载的副作用执行顺序导致最终状态被强制重置为1
关于两个疑问的解答
1. 是否可以在useState初始化函数中读取LocalStorage或请求API?
- 读取LocalStorage是完全可行且推荐的:虽然官方要求初始化函数是纯函数,但读取LocalStorage属于同步只读操作,不会修改外部状态,且初始化函数仅在组件第一次渲染时执行一次,不会影响组件的可预测性。这是React社区普遍认可的用法。
- 不能在初始化函数中请求API:API请求是异步操作,而初始化函数必须同步返回初始值,无法等待异步结果。异步数据获取应放在
useEffect中执行。
2. 正确的实现方式
方式一:使用useState初始化函数(推荐)
直接在useState的初始化函数中读取LocalStorage,获取初始值,避免额外的useEffect和重渲染:
import ReactDOM from "react-dom/client"; import React, { useState, useEffect } from "react"; const App = () => { // 从LocalStorage读取初始值,不存在则用默认值1 const [radio, setRadio] = useState(() => { const localRadio = localStorage.getItem('radio'); return localRadio ? +localRadio : 1; }); // 状态变化时写入LocalStorage useEffect(() => { localStorage.setItem('radio', radio); }, [radio]); const radioChangeHandler = (event) => { setRadio(+event.target.value); }; return ( <div> <h1>useState initializer demo</h1> <div className="radio-group" onChange={radioChangeHandler}> <input type="radio" value="1" checked={radio === 1} id="language1" name="languageChoice" /> <label htmlFor="language1">Javascript</label> <input type="radio" value="2" checked={radio === 2} id="language2" name="languageChoice" /> <label htmlFor="language2">HTML</label> <input type="radio" value="3" checked={radio === 3} id="language3" name="languageChoice" /> <label htmlFor="language3">CSS</label> </div> </div> ); }; const container = document.querySelector("#root"); const root = ReactDOM.createRoot(container); root.render( <React.StrictMode> <App /> </React.StrictMode> );
方式二:封装自定义Hook(复用场景推荐)
如果多个组件需要同步LocalStorage状态,可以封装一个useLocalStorage Hook,提高代码复用性:
import React, { useState, useEffect } from "react"; function useLocalStorage(key, initialValue) { // 初始化读取LocalStorage const [value, setValue] = useState(() => { const storedValue = localStorage.getItem(key); return storedValue !== null ? JSON.parse(storedValue) : initialValue; }); // 状态变化时更新LocalStorage useEffect(() => { localStorage.setItem(key, JSON.stringify(value)); }, [key, value]); return [value, setValue]; } // 使用自定义Hook const App = () => { const [radio, setRadio] = useLocalStorage('radio', 1); const radioChangeHandler = (event) => { setRadio(+event.target.value); }; return ( <div> <h1>useLocalStorage demo</h1> <div className="radio-group" onChange={radioChangeHandler}> <input type="radio" value="1" checked={radio === 1} id="language1" name="languageChoice" /> <label htmlFor="language1">Javascript</label> <input type="radio" value="2" checked={radio === 2} id="language2" name="languageChoice" /> <label htmlFor="language2">HTML</label> <input type="radio" value="3" checked={radio === 3} id="language3" name="languageChoice" /> <label htmlFor="language3">CSS</label> </div> </div> ); };
这种方式不仅解决了StrictMode下的问题,还让状态与LocalStorage的同步逻辑可复用,代码更简洁。
内容的提问来源于stack exchange,提问作者Tun Huang
相关产品推荐
相关产品推荐

