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

React Game Provider变量在逻辑函数中无法更新的问题

问题分析与解决方案

问题核心

你遇到的问题是:initializeGame函数里拿到的gameEngine对象是组件第一次渲染时的快照,后续React状态更新后,这个对象里的isReady、text始终是旧值,导致轮询永远无法检测到状态变化。

之前多轮测试无效的原因:

  • getReady函数:因为闭包陷阱,它捕获的是第一次渲染时的isReady状态,后续状态更新后,函数依然返回初始的false。
  • 对象包裹isReady:第一次渲染时gameEngine里的对象是初始引用,后续状态更新时组件会创建新的gameEngine对象,但initializeGame里持有的是旧对象,旧对象的value从未被更新。
  • useRef存储isReady:如果只是把isReady改成Ref,gameEngine里的isReady是ref.current的初始快照(false),后续ref.current变化后,旧gameEngine里的isReady依然是旧值,除非用Ref保存整个gameEngine的最新引用。

根本原因:
当setReady(true)触发GameEngineProvider重新渲染时,组件会创建一个全新的gameEngine对象,但initializeGame里持有的是第一次渲染时传入的旧对象。而setText能正常工作,是因为React的setState函数是稳定的,不会随组件重新渲染而变化,旧对象里的setText依然能触发状态更新。

解决方案

方案1:用Ref保存最新的GameEngine引用

通过useRef保存最新的gameEngine对象,让initializeGame能随时访问到最新状态:

修改GameEngineProvider代码:

export const GameEngineProvider = ({ children }: PropsWithChildren): JSX.Element => {
  // Engine States
  const isLoaded = useRef<boolean>(false);
  const [isReady, setReady] = useState<boolean>(false);
  const [text, setText] = useState<string>("");

  // Example of other hook provided by GameEngine
  const player = usePlayer();

  // 用Ref保存最新的gameEngine对象
  const gameEngineRef = useRef<GameEngineContextType>({} as GameEngineContextType);

  // 每次渲染都更新Ref,确保它指向最新的状态和函数
  const gameEngine: GameEngineContextType = {
    isReady,
    getReady: () => isReady,
    text, 
    setText,
    updateGameStep: () => { /* Go to the next game step */ },
    player,
  };
  gameEngineRef.current = gameEngine;

  useEffect(() => {
    if (!isLoaded.current) {
      isLoaded.current = true;
      // 传入Ref而不是静态对象
      initializeGame(gameEngineRef);

      setTimeout(() => {
        console.log("SET Ready");
        setReady(true);
      }, 2000);
    }
  }, []);

  return (
    <GameEngineContext.Provider value={gameEngine}>
      {children}
    </GameEngineContext.Provider>
  );
};

修改initializeGame和waitForCondition,从Ref里获取最新状态:

export const initializeGame = async (gameEngineRef: React.MutableRefObject<GameEngineContextType>) => {
  gameEngineRef.current.setText("Loading...");

  // Wait GameEngine to be ready
  await waitForCondition(() => {
    const latestEngine = gameEngineRef.current;
    console.log("Is game ready: ", latestEngine.text, latestEngine.isReady, latestEngine.getReady());
    return latestEngine.isReady;
  });

  // Call function to go on the next step
  gameEngineRef.current.updateGameStep();
};

方案2:将initializeGame改为组件内部函数

直接在GameEngineProvider内部定义initializeGame,让它能直接访问组件内的最新状态,避免闭包问题:

export const GameEngineProvider = ({ children }: PropsWithChildren): JSX.Element => {
  // Engine States
  const isLoaded = useRef<boolean>(false);
  const [isReady, setReady] = useState<boolean>(false);
  const [text, setText] = useState<string>("");

  // Example of other hook provided by GameEngine
  const player = usePlayer();

  // 内部定义initializeGame,直接访问最新状态
  const initializeGame = async () => {
    setText("Loading...");

    await waitForCondition(() => {
      console.log("Is game ready: ", text, isReady);
      return isReady;
    });

    updateGameStep();
  };

  const updateGameStep = () => { /* Go to the next game step */ };

  useEffect(() => {
    if (!isLoaded.current) {
      isLoaded.current = true;
      initializeGame();

      setTimeout(() => {
        console.log("SET Ready");
        setReady(true);
      }, 2000);
    }
  }, []);

  const gameEngine: GameEngineContextType = {
    isReady,
    getReady: () => isReady,
    text, 
    setText,
    updateGameStep,
    player,
  };

  return (
    <GameEngineContext.Provider value={gameEngine}>
      {children}
    </GameEngineContext.Provider>
  );
};

验证效果

修改后,initializeGame里的轮询会正确检测到isReady变为true,日志会输出:

Is game ready:  Loading... false false
Is game ready:  Loading... false false
SET Ready
Is game ready:  Loading... true true

同时GameData组件依然能正常显示状态变化。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.29 21:38:09