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

React原始状态未改变时为何会触发重新渲染?

为什么调用setCounter(相同值)仍会触发重新渲染?

先看你提供的React代码:

function App(props) {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    document.addEventListener('click', () => {
      setCounter(counter+1);
    });
  }, []);

  console.log('render', counter);
  return (
    <div className="App">
      <h1>{counter}</h1>
    </div>
  );
}

你已经发现useEffect的依赖问题:因为依赖项是空数组,事件处理函数捕获的是初始的counter=0,所以第一次点击会把counter设为1,第二次点击还是会调用setCounter(0+1)=1——而此时counter已经是1,但依然触发了渲染,控制台输出render 1。

核心原因

React的默认行为是:只要调用了状态更新函数(比如setCounter),无论新值和当前状态是否相同,都会触发组件重新渲染。

React并不会自动对新旧状态值做浅比较来跳过渲染,这个步骤需要你手动通过优化手段实现:

  • 函数组件可以用React.memo包裹组件,它会对props做浅比较,若props未变化则跳过渲染;
  • 类组件可以使用React.PureComponent或者自定义shouldComponentUpdate方法来控制渲染时机。

不过要注意:即使触发了这次渲染,最终DOM并不会有任何更新——因为React会在渲染后对比虚拟DOM,发现内容没有变化,就会跳过真实DOM的更新操作,所以用户看不到界面变化,但组件函数本身还是会执行,控制台的console.log会打印出来。

顺便修复原bug

回到你的代码,要解决事件处理函数捕获过时状态的问题,有两种方案:

  1. 把counter加入useEffect的依赖数组,这样每次counter变化时都会重新绑定事件处理函数:
useEffect(() => {
  const handleClick = () => {
    setCounter(counter + 1);
  };
  document.addEventListener('click', handleClick);
  return () => document.removeEventListener('click', handleClick); // 别忘了清理事件监听
}, [counter]);
  1. 使用函数式更新,直接获取最新的状态值,无需依赖counter:
useEffect(() => {
  document.addEventListener('click', () => {
    setCounter(prevCounter => prevCounter + 1);
  });
}, []);

第二种方案更优,因为不需要频繁绑定/解绑事件,而且总能拿到最新的状态值。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.25 08:03:39