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

React函数组件中useState变量在EventListener中不更新的问题

问题原因:React闭包陷阱导致事件监听器无法获取最新props值

这是典型的React闭包陷阱问题,我来给你拆解一下为什么会这样:

核心问题分析

你的Child组件里,绑定事件监听器的useEffect依赖数组是空的[],这意味着这个effect只会在组件第一次挂载时执行一次。此时添加的mousedown监听器对应的md函数,是组件第一次渲染时创建的版本——它捕获了当时的num值(也就是父组件初始传入的50),以及作用域里的x变量引用。

当你点击父组件的Test按钮更新num时:

  1. Child组件会重新渲染,生成新的num参数、新的x变量和新的md函数;
  2. 第二个useEffect(依赖num的那个)会把新的num赋值给当前渲染周期的x,但这个x和旧的md函数里引用的x是两个完全不同的变量;
  3. 旧的事件监听器仍然存在,它调用的还是第一次渲染时的md函数,所以打印的始终是最初的num值,以及旧的x(最后被赋值为初始的50)。

解决方案:两种常用思路

方案1:让事件监听器随num更新而重新绑定

把num加入到绑定监听器的useEffect依赖数组中,这样每次num变化时,都会移除旧的监听器,绑定新的md函数(此时新的md会捕获最新的num值):

const Child = ({ num }) => {
  const md = () => {
    console.log(num); // 直接用num就行,不需要额外的x变量
  };

  useEffect(() => {
    const box = document.getElementById("box");
    box.addEventListener("mousedown", md);
    return () => {
      box.removeEventListener("mousedown", md);
    };
  }, [num]); // 这里添加num作为依赖

  return <div id="box">click {num}</div>;
};

这个方案简单直接,适合大多数场景,唯一的小代价是每次num变化时会重新绑定一次事件,但这种开销几乎可以忽略。

方案2:用useRef保存最新值(避免频繁解绑绑定)

如果不想每次num变化都重新绑定监听器,可以用useRef来存储最新的num值——useRef的.current属性是可变的,不会触发组件渲染,而且能被闭包持续访问:

import React, { useState, useEffect, useRef } from "react";

const Child = ({ num }) => {
  // 创建ref保存最新的num值
  const numRef = useRef(num);

  // 每次num更新时,同步更新ref的current值
  useEffect(() => {
    numRef.current = num;
  }, [num]);

  const md = () => {
    console.log(numRef.current); // 这里访问的始终是最新的num
  };

  useEffect(() => {
    const box = document.getElementById("box");
    box.addEventListener("mousedown", md);
    return () => {
      box.removeEventListener("mousedown", md);
    };
  }, []); // 依赖数组保持为空,只绑定一次

  return <div id="box">click {num}</div>;
};

这个方案适合需要长期绑定事件、且状态更新频繁的场景,避免了重复解绑绑定的操作。


额外建议

你代码里的x变量其实是多余的,它只是用来复制num的值,完全可以直接在md里使用num,减少不必要的变量带来的混淆。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.13 08:58:36