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

React未用useCallback包裹的事件处理器取值与重渲染问题

React 普通事件函数状态获取问题解析

示例代码如下:

import { StrictMode } from "react";
import { createRoot } from "react-dom/client";

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

const App = () => {
  const [channel, setChannel] = useState(null);

  const handleClick1 = useCallback(() => {
    console.log(channel);
  }, [channel]);

  const handleClick2 = () => {
    console.log(channel);
  };

  const parentClick = () => {
    console.log("parent is call");
    setChannel((prev) => prev + 1);
  };
  // useEffect(() => {
  //   setChannel(1);
  // });
  return (
    <div className="App">
      <button onClick={parentClick}>parent click</button>
      <br />
      <br />
      <button onClick={handleClick1}>child click1</button>
      <button onClick={handleClick2}>child click2</button>
      &nbsp;
    </div>
  );
};

const rootElement = document.getElementById("root");
const root = createRoot(rootElement);
root.render(<App />);

上述代码中定义了两个按钮点击事件处理函数:handleClick1通过useCallback包裹并将channel加入依赖数组,handleClick2未做Hook包裹。点击父按钮触发channel状态更新后,两个点击处理函数都能获取到正确的channel状态值,针对handleClick2的两个疑问解答如下:


问题1:channel状态值变化是否会触发该函数重新求值,进而导致整个UI重渲染?

答案是否定的,这里完全搞反了因果顺序:

  • 触发UI重渲染的唯一原因是setChannel执行后更新了channel状态,和handleClick2没有关系。React里触发组件重渲染的场景只有三类:组件自身状态更新、父组件重渲染、订阅的context值更新,重渲染本质就是把组件函数从头到尾重新执行一遍。
  • handleClick2被重新创建(也就是问题里说的重新求值),只是组件重渲染过程中自然产生的结果,根本不是重渲染的诱因。按照JS的基础执行逻辑,函数每次运行时,内部定义的普通变量、普通函数都会重新生成一遍,组件函数也遵循这个规则。

问题2:handleClick2是如何获取到正确的channel状态值的?

核心逻辑非常简单,每一次组件渲染都有属于自己的独立闭包:

  • 每一轮组件重渲染执行时,都会生成当前渲染周期专属的channel值,这个值在当前这轮渲染的整个生命周期里都是固定不变的。
  • 这轮渲染里新定义的handleClick2,本身就是在当前组件函数的执行上下文中创建的,它的闭包捕获的自然就是当前这轮渲染对应的最新channel值。
  • 最终渲染到页面上的按钮,onClick属性绑定的就是这轮渲染新生成的handleClick2,点击按钮时调用的就是这个新函数,当然能拿到最新的状态值。

补充说明:很多人对useCallback存在误解,觉得它是用来解决闭包拿不到最新值的问题,这完全是错误认知。useCallback的核心作用是缓存函数引用——只要依赖项不变,它就会返回和上一轮渲染完全相同的函数对象,避免把函数作为props传给做了引用相等判断的子组件时,因为每次渲染生成新函数引用导致子组件做不必要的重渲染。反过来如果给useCallback漏传了依赖,缓存的旧函数闭包捕获的是老状态,反而会出现拿不到最新值的bug。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.29 22:24:24