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

React Context Provider中useState数据在子组件卸载后丢失如何修复

问题原因
  • 核心原因是你直接修改了React状态的原始引用,没有遵循不可变更新原则:你在getDataFromStock中直接对原stocks数组、数组内的stock对象做了属性修改,再将原引用传给setStocks。React状态更新依赖引用变化做浅比较,这种突变写法不会触发可靠的状态持久化,当Provider因路由切换等场景重渲染时,未被正确记录的突变内容(也就是你加的data字段)就会丢失。
  • 次要问题:useCallback依赖stocks会导致getDataFromStock实例每次stocks变化时重新生成,进而触发详情页useEffect重复执行,还可能出现闭包陷阱拿到旧的stocks值。
  • 额外隐患:初始stocks状态为undefined,未做空判断直接调用find方法会抛出运行时错误。
修复方案

1. 修正StockProvider代码

核心改动点:使用状态更新的回调语法拿到最新状态,采用不可变方式生成新的状态数组/对象,移除useCallback对stocks的依赖。

import { createContext, useState, useCallback, useMemo, useContext } from "react";
import axios from "axios";

export const StockContext = createContext();
export const useStock = () => useContext(StockContext);

// 建议这里填入你初始的基础股票数据,至少设为空数组
const INITIAL_STOCKS = [{id: "a", name: "a"}];

export const StockProvider = ({ children }) => {
    // 初始化状态为基础数据,避免undefined报错
    const [stocks, setStocks] = useState(INITIAL_STOCKS);

    const getDataFromStock = useCallback(
        async ({ id }) => {
            let method = "GET";
            let url = `${config.base_url}data/${id}`;
            try {
                const { data: response } = await axios({ method, url });
                const { succes, data, error } = response;
                // 使用函数式更新,拿到最新的prevStocks,无需依赖外部stocks
                setStocks(prevStocks => {
                    // 不可变更新:返回新数组,匹配id的项返回新对象挂载data
                    return prevStocks.map(stock => 
                        stock.id === id ? {...stock, data} : stock
                    )
                });
                return succes;
            } catch(err) {
                // 建议补充错误处理
                return false;
            }
        },
        // 移除stocks依赖,函数实例不会频繁变化
        []
    );

    const value = useMemo(
        () => ({
            getDataFromStock,
            stocks,
        }),
        [getDataFromStock, stocks]
    );

    return <StockContext.Provider value={value}>{children}</StockContext.Provider>;
};

2. 修正StockDetailPage代码

核心改动点:补充空值判断,避免运行时报错。

export default function StockDetailPage() {
    const { id } = useParams();
    const [currentStock, setCurrentStock] = useState({});
    const { stocks, getDataFromStock } = useContext(StockContext);

    useEffect(() => {
        const getData = async () => {
            // 先判断stocks存在,再找对应id的stock,再判断data是否存在
            const targetStock = stocks?.find(s => s.id === id);
            if (!targetStock) return;
            if (!targetStock.data) {
                console.log("getting");
                await getDataFromStock({ id });
            } else {
                console.log("not getting");
            }
        };
        getData();
    }, [getDataFromStock, id, stocks]);

    return (
        <div>
            Stock: {currentStock?.owner?.username}
            {stocks
                ?.find((s) => s.id === id)
                ?.data?.map((p) => (
                    <ProductPreview key={p.product_id} {...p} />
                ))}
        </div>
    );
}
效果说明

修改后所有状态更新都会生成新的引用,React可以正确持久化你新增的data字段,哪怕路由切换页面卸载,只要StockProvider没有被卸载,缓存的data字段就会一直保留,下次进入详情页无需重复请求接口。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.25 23:06:02