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

React用localStorage持久化组件切换时的猫咪签到状态

React路由切换后状态丢失,使用localStorage持久化猫咪签到状态

问题根因

你现在的代码有三个核心问题导致状态丢失:

  • localStorage存储逻辑和签到状态完全脱节,存的是无意义的默认值,根本没有保存实际的签到数据
  • 大量直接操作DOM修改按钮文本、样式的写法不符合React状态驱动的设计,组件卸载重挂后DOM会重置为初始模板内容,自然看不到之前的状态
  • 没有在组件加载时读取本地存储的内容还原状态

修复方案

按照下面的逻辑改就可以实现状态持久化:

  • 用统一的对象结构存储所有猫咪的签到状态,方便序列化存入localStorage
  • 组件初始化时优先读取localStorage里的存储值作为状态初始值
  • 监听签到状态变化,每次更新自动同步到localStorage
  • 所有UI展示(按钮文字、状态文本、按钮颜色)全部由状态驱动,不要直接操作DOM

完整修复代码

import "./CatSign.css";
import React, { useState, useEffect } from "react";
import SignedIn from "./SignedIn.js";
import whiteCat from "./Pics/whiteCat.jpg";
import spotty from "./Pics/spotty.jpg";
import small from "./Pics/small.jpg";
import clouded from "./Pics/cloudedleopard.jpg";
import leopard from "./Pics/leopard.jpg";
import tiger from "./Pics/tiger.jpg";
import whiteTiger from "./Pics/whiteTiger.jpg";

// 本地存储key,抽成常量避免写错
const STORAGE_KEY = "cat_signin_records";
const catList = ["Mittens", "Bao", "Amber", "Tash", "Kasia", "Luna", "Spike"];
const catPhotos = [whiteCat, spotty, small, clouded, leopard, tiger, whiteTiger];

// 生成默认签到状态:所有猫咪默认未签到
const initDefaultStatus = () => {
  return catList.reduce((res, name) => {
    res[name] = false;
    return res;
  }, {});
};

export default function CatSign(props) {
  const [listVisible, setListVisible] = useState(true);
  const [activeCat, setActiveCat] = useState(null);

  // 初始化状态:优先读本地存储,没有就用默认值
  const [signRecords, setSignRecords] = useState(() => {
    const saved = localStorage.getItem(STORAGE_KEY);
    return saved ? JSON.parse(saved) : initDefaultStatus();
  });

  // 状态变化自动同步到本地存储
  useEffect(() => {
    localStorage.setItem(STORAGE_KEY, JSON.stringify(signRecords));
  }, [signRecords]);

  // 切换单只猫咪签到状态
  const toggleCatSign = (catName) => {
    const newStatus = !signRecords[catName];
    setSignRecords(prev => ({...prev, [catName]: newStatus}));
    
    if (newStatus) {
      setActiveCat(catName);
      setListVisible(false);
    } else {
      setActiveCat(null);
      setListVisible(true);
    }
  };

  if (!props.dailyLog) return null;

  return (
    <div className="CatSign">
      <div
        className="catsExpected"
        style={{ display: listVisible ? "block" : "none" }}
      >
        You have {catList.length} cats expected today
      </div>
      {catList.map((catName, index) => {
        const signedIn = signRecords[catName];
        return (
          <div
            className="childContainer"
            key={catName}
            style={{ display: listVisible ? "flex" : "none" }}
          >
            <img className="catPic" src={catPhotos[index]} alt="Cat"></img>
            <p className="cat"> {catName} </p>
            <p className="status">{signedIn ? "Signed In" : "Signed Out"}</p>
            <button 
              className="signInOut" 
              onClick={() => toggleCatSign(catName)}
              style={{ background: signedIn ? "#feffff" : "#f0677a" }}
            >
              {signedIn ? "Sign Out" : "Sign In"}
            </button>
          </div>
        );
      })}
      <SignedIn
        signIn={!!activeCat}
        setSignIn={(isSignIn) => {
          if (!isSignIn && activeCat) {
            setSignRecords(prev => ({...prev, [activeCat]: false}));
            setActiveCat(null);
            setListVisible(true);
          }
        }}
        cat={activeCat}
        setIsShown={setListVisible}
      />
    </div>
  );
}

额外说明

  • useState初始化时用函数读取localStorage,能避免组件每次渲染都重复读取本地存储,性能更好
  • 不要依赖DOM内容判断当前状态(比如你之前判断按钮innerText是不是Sign In),所有状态都存在React state里,UI完全跟着状态走,就不会出现切换页面后状态重置的问题
  • 现在不管怎么切换路由组件,只要浏览器本地存储不清除,签到状态都会一直保留

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 19:15:28