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

React Context值在useEffect中未更新及请求循环问题咨询

React+TypeScript开发中的两个问题解析

问题现象

  1. 在useEffect中调用setInitialData更新状态后,立即console.log(initialData)输出的仍是null
  2. 代码持续发起HTTP请求,陷入无限循环

代码片段

Context代码(InitialDataContext.tsx)

import React, {
    Dispatch,
    SetStateAction,
    createContext,
    useContext,
    useState,
} from 'react';

interface InitialDataType {
    GameName: string;
    GameUrl: string;
    HasRegulation: boolean;
    HideNavBar: boolean;
    HideSessionNetLoss: boolean;
    HideSessionTimer: boolean;
    RegulationAction: string;
    RegulationModel?: string;
    TournamentId?: number;
    TournamentType?: number;
    TournamentsBaseUrl?: string;
    Url: string;
    VendorController: string;
}

interface InitialDataContextInterface {
    initialData: InitialDataType | null;
    setInitialData: Dispatch<SetStateAction<InitialDataType | null>>;
}

const Context = createContext({} as InitialDataContextInterface);

const InitialDataProvider = ({ children }) => {
    const [initialData, setInitialData] = useState<InitialDataType | null>(
        null
    );

    return (
        // eslint-disable-next-line react/jsx-no-constructed-context-values
        <Context.Provider value={{ initialData, setInitialData }}>
            {children}
        </Context.Provider>
    );
};

export default InitialDataProvider;

export const useInitialData = () => useContext(Context);

Home组件代码(Home.tsx)

import React, { useContext, useEffect } from 'react';
import axios from 'axios';
import UkRegulation from '../../regulations/UkRegulation/UkRegulation';
import GameIframe from '../../components/GameIframe/GameIframe';
import { useInitialData } from '../../context/InitialDataContext';

const Home = () => {
    const { initialData, setInitialData } = useInitialData();

    useEffect(() => {
        axios
            .get(
                'https://myUrl.com/my-endpoint'
            )
            .then((response) => {
                setInitialData(response.data);
                console.log('InitialData', initialData);
            })
            .catch((error) => {
                console.log(error);
            });
    }, []);

    return (
        <div>
            <UkRegulation />
            <GameIframe />
        </div>
    );
};

export default Home;

问题原因

1. console.log输出null的原因

React的useState状态更新是异步的,且闭包特性会让useEffect回调函数捕获组件首次渲染时的initialData值(也就是初始的null)。即便调用setInitialData触发了状态更新,当前回调里的initialData并不会立即改变——它仍然是闭包中保存的旧值,所以输出null。

2. 无限循环请求的原因

你的InitialDataProvider组件每次渲染时,都会创建一个全新的{ initialData, setInitialData }对象作为Provider的value。当initialData更新后,Provider会重新渲染,导致value引用变化,所有消费该Context的组件(包括Home)都会被迫重新渲染。如果Home的子组件(如UkRegulation或GameIframe)中也存在发起请求的逻辑,就会形成循环请求;另外,若不慎将initialData或setInitialData加入useEffect依赖数组,也会触发重复执行,但你的代码中依赖为空数组,所以核心原因是Context的value引用不稳定导致的频繁组件渲染。

解决方案

1. 查看更新后的initialData值

有两种方式可以获取到最新的状态:

  • 方式一:在组件顶级作用域打印:组件每次重新渲染都会执行这里的代码,能输出最新的initialData
const Home = () => {
    const { initialData, setInitialData } = useInitialData();
    console.log('最新InitialData:', initialData); // 这里会输出更新后的值

    useEffect(() => {
        axios.get('https://myUrl.com/my-endpoint')
            .then(res => setInitialData(res.data))
            .catch(err => console.log(err));
    }, []);
    // ...
};
  • 方式二:用useEffect监听状态变化:当initialData更新时,自动执行打印逻辑
const Home = () => {
    const { initialData, setInitialData } = useInitialData();

    useEffect(() => {
        axios.get('https://myUrl.com/my-endpoint')
            .then(res => setInitialData(res.data))
            .catch(err => console.log(err));
    }, []);

    // 监听initialData变化,打印最新值
    useEffect(() => {
        console.log('最新InitialData:', initialData);
    }, [initialData]);

    // ...
};

2. 解决无限循环请求问题

核心是让Context的value保持稳定引用,避免不必要的组件重新渲染:使用useMemo缓存value对象,只有当initialData变化时才重新创建:

// 修改InitialDataProvider组件
import React, {
    Dispatch,
    SetStateAction,
    createContext,
    useContext,
    useState,
    useMemo, // 引入useMemo
} from 'react';

// ... 其他接口定义不变

const InitialDataProvider = ({ children }) => {
    const [initialData, setInitialData] = useState<InitialDataType | null>(null);

    // 用useMemo缓存context value,仅当initialData变化时更新
    const contextValue = useMemo(() => ({
        initialData,
        setInitialData
    }), [initialData]); // useState返回的setInitialData是稳定引用,无需加入依赖

    return (
        <Context.Provider value={contextValue}>
            {children}
        </Context.Provider>
    );
};

同时检查UkRegulation和GameIframe组件,确保它们没有重复发起相同请求的逻辑。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.13 04:42:07