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

React项目中Local Storage重载后状态丢失,原因是什么?

问题分析与解决方案

你的代码已实现localStorage存储逻辑,但页面刷新后状态丢失主要有以下几个原因:

核心问题点

  1. 状态初始化时机错误:依赖useEffect在组件挂载后读取localStorage并设置状态,但组件首次渲染会先使用useState的初始值(空数组和null),后续才更新状态。这不仅会导致首次渲染显示空状态,还可能引发逻辑异常。
  2. activeItem序列化问题:直接存入localStorage时,null会被自动转为字符串'null',读取时无法正确还原为null类型。
  3. 缺少ActiveComponent定义:未根据activeItem从openItems中获取对应组件,导致活跃项无法渲染。

修改后的完整代码

const Projects = () => {
    const [allProjectsOpen, setAllProjectsOpen] = useState(false);
    const [reactProjectsOpen, setReactProjectsOpen] = useState(false);
    const [angularProjectsOpen, setAngularProjectsOpen] = useState(false);

    const projectItemsRef = useRef([
        {
            name: '_photo_portfolio',
            icon: <RiJavascriptFill className="w-6 h-6 text-custom-blue" />,
            type: 'react',
            component: PhotoPortfolio,
        },
        {
            name: '_angul_it',
            icon: <FaAngular className="w-6 h-6 text-custom-purple" />,
            type: 'angular',
            component: AngulIt,
        },
        {
            name: '_unknown_project',
            icon: <RiJavascriptFill className="w-6 h-6 text-custom-blue" />,
            type: 'react',
            component: UnknownProject,
        },
    ]);

    // 直接从localStorage初始化状态,替代useEffect读取
    const [openItems, setOpenItems] = useState(() => {
        const storedOpenItemNames = JSON.parse(localStorage.getItem('openItemNames')) || [];
        return projectItemsRef.current.filter(item => storedOpenItemNames.includes(item.name));
    });

    const [activeItem, setActiveItem] = useState(() => {
        // 用JSON序列化还原null类型
        const storedActiveItem = JSON.parse(localStorage.getItem('activeItemName'));
        return storedActiveItem ?? null;
    });

    // Save state to localStorage
    useEffect(() => {
        const openItemNames = openItems.map(item => item.name);
        localStorage.setItem('openItemNames', JSON.stringify(openItemNames));
        // 用JSON序列化存储,保留null类型
        localStorage.setItem('activeItemName', JSON.stringify(activeItem));
    }, [openItems, activeItem]);

    // Add item to openItems
    const addItem = (item) => {
        setOpenItems((prevItems) => {
            if (prevItems.some(prevItem => prevItem.name === item.name)) {
                return prevItems;
            }
            return [...prevItems, item];
        });
        setActiveItem(item.name);
    };

    // Remove item from openItems
    const removeItem = (itemName) => {
        setOpenItems((prevItems) => prevItems.filter((prevItem) => prevItem.name !== itemName));
        if (activeItem === itemName) {
            setActiveItem(null);
        }
    };

    // 定义ActiveComponent,根据activeItem获取对应组件
    const ActiveComponent = openItems.find(item => item.name === activeItem)?.component;

    return (
        <div>
            <div>
                <ItemList
                    items={openItems}
                    activeItem={activeItem}
                    setActiveItem={setActiveItem}
                    removeItem={removeItem} />
                <div>
                    {ActiveComponent && <ActiveComponent />}
                </div>
            </div>
        </div>
    );
};

关键修改说明

  1. 状态初始化逻辑迁移:将原本在useEffect中的读取逻辑移到useState的初始化函数中,确保组件首次渲染就使用localStorage中的状态值。
  2. JSON序列化处理:存储和读取activeItem时使用JSON.stringify和JSON.parse,保证null类型能被正确保存和还原。
  3. 补充ActiveComponent定义:通过openItems.find匹配activeItem对应的组件,解决活跃项无法渲染的问题。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.20 13:28:09