You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.17 06:35:37