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

React中如何为每个独立项在localStorage存储状态?解决刷新状态异常

React 状态持久化问题:刷新后所有贴纸状态统一

我在React开发中遇到一个问题:保存组件状态后刷新页面,所有组件的状态都会被设置为同一个值。需要为每个唯一的贴纸项设置true或false的状态并持久化保存,在React DevTools中点击每个项时状态能正常切换,但显然我的保存方式存在错误。

相关代码

父组件

const AllStickers = ({ countries, setStickersCollected }) => {
  return (
    <main>
      
      {countries.map((country, index) => {
        return (
          <div key={index} className='stickers-section'>
            <CountryTitle title={country} />
            <div className='stickers'>
              {stickers.map((item, index) => {
                return (
                  <StickerNumber
                    key={index}
                    number={item}
                    setStickersCollected={setStickersCollected}
                  />
                );
              })}
            </div>
          </div>
        );
      })}
    </main>
  );
};

export default AllStickers;

子组件

const StickerNumber = ({ number, setStickersCollected }) => {
  const [isCollected, setIsCollected] = useState(
    JSON.parse(localStorage.getItem('isCollected')) || false
  );

  useEffect(() => {
    window.localStorage.setItem('isCollected', JSON.stringify(isCollected));
  }, [isCollected]);

  const handleClick = (e) => {
    setIsCollected(!isCollected);
    if (isCollected === false) {
      setStickersCollected((prev) => prev + 1);
    } else if (isCollected === true) {
      setStickersCollected((prev) => prev - 1);
    }
  };

  return (
    <p className='sticker-number' onClick={handleClick}>
      {number}
      <span
        className='checkmark'
        style={{ display: isCollected === false ? 'none' : 'block' }}
      >
        <FcCheckmark />
      </span>
    </p>
  );
};

export default StickerNumber;

问题根源

所有StickerNumber组件都共用同一个localStorage键isCollected,最后更新的贴纸状态会覆盖之前的存储值。刷新页面时,所有组件都会读取同一个键的值,导致状态统一。

解决方案

1. 给每个贴纸生成唯一存储键

结合贴纸的number和所属国家country生成唯一键,确保每个贴纸的状态存储互不干扰。

2. 修改父组件传递参数

给子组件传递当前贴纸所属的country:

// 父组件中修改StickerNumber的props
{stickers.map((item, index) => {
  return (
    <StickerNumber
      key={index}
      number={item}
      country={country} // 新增传递country参数
      setStickersCollected={setStickersCollected}
    />
  );
})}

3. 子组件调整状态逻辑

使用唯一键读取和存储状态,同时修正点击事件的异步状态判断问题:

const StickerNumber = ({ number, country, setStickersCollected }) => {
  // 生成唯一存储键
  const storageKey = `sticker-collected-${country}-${number}`;
  const [isCollected, setIsCollected] = useState(
    JSON.parse(localStorage.getItem(storageKey)) || false
  );

  useEffect(() => {
    window.localStorage.setItem(storageKey, JSON.stringify(isCollected));
  }, [isCollected, storageKey]);

  const handleClick = (e) => {
    const newCollectedState = !isCollected;
    setIsCollected(newCollectedState);
    // 直接用新状态判断,避免setState异步导致的旧值问题
    setStickersCollected(prev => prev + (newCollectedState ? 1 : -1));
  };

  return (
    <p className='sticker-number' onClick={handleClick}>
      {number}
      <span
        className='checkmark'
        style={{ display: isCollected ? 'block' : 'none' }}
      >
        <FcCheckmark />
      </span>
    </p>
  );
};

export default StickerNumber;

额外优化建议

  • 避免用index作为组件的key,如果country或item有唯一标识(比如国家代码、贴纸ID),改用这些唯一值当key,减少不必要的组件重渲染。

内容的提问来源于stack exchange,提问作者Dinko Dinic

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.20 02:17:35