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

React中事件监听器内Context值未同步更新的问题

React Context更新后事件监听器未同步取值的问题解决

问题原因

你遇到的是典型的闭包陷阱:

  • 你的useEffect依赖数组是空的[],这意味着它只会在组件首次挂载时执行一次。
  • 此时事件监听器捕获的是组件首次渲染时的gameData(值为0),后续Context更新导致gameData变化时,这个闭包不会被更新,所以监听器内部始终使用旧值。
  • 而组件外部的console.log(gameData.board)能拿到新值,是因为组件重新渲染时会读取最新的Context值。

解决方案

方案1:将依赖项加入useEffect的依赖数组

把gameData和updateGame添加到依赖数组中,每次依赖变化时重新绑定事件监听器,同时记得清理旧监听器:

const updateGame = useUpdateGame();
const gameData = useGameData(); 

useEffect(() => {
  const handleKeyUp = (e) => {
    e.stopImmediatePropagation();
    console.log(gameData.board); // 现在能获取最新值
    handleKeyPress(e.key, gameData.board, updateGame);
  };

  document.addEventListener("keyup", handleKeyUp);
  
  // 清理旧监听器,避免内存泄漏
  return () => {
    document.removeEventListener("keyup", handleKeyUp);
  };
}, [gameData, updateGame]); // 添加依赖项

这样每次gameData或updateGame更新时,都会生成新的handleKeyUp函数,绑定的监听器会捕获最新的Context值。

方案2:用useRef保存最新值

如果不想频繁解绑/绑定监听器,可以用useRef存储最新的gameData,避免闭包问题:

const updateGame = useUpdateGame();
const gameData = useGameData(); 
const gameDataRef = useRef(gameData);

// 同步更新ref的current值
useEffect(() => {
  gameDataRef.current = gameData;
}, [gameData]);

useEffect(() => {
  const handleKeyUp = (e) => {
    e.stopImmediatePropagation();
    console.log(gameDataRef.current.board); // 读取ref中的最新值
    handleKeyPress(e.key, gameDataRef.current.board, updateGame);
  };

  document.addEventListener("keyup", handleKeyUp);
  
  return () => {
    document.removeEventListener("keyup", handleKeyUp);
  };
}, [updateGame]);

useRef的current属性是可变的,不会触发组件重渲染,事件监听器直接访问gameDataRef.current就能拿到最新的Context值。

注意事项

无论用哪种方案,都必须在useEffect的清理函数中移除事件监听器,防止组件卸载后监听器残留导致内存泄漏。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.13 17:46:27