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

React useEffect清理函数内state变量取值不符合预期问题咨询

React useEffect 清理函数状态值不符合预期问题解析

问题复现

以下是通过 create-react-app 搭建的 React 项目中 App.js 的实现代码:

import { useState, useEffect } from "react";
 
function App() {
  const [pState, setPState] = useState(false);
 
  useEffect(() => {
    console.log('Use effect parent. ' + pState);
 
    return () => {
      console.log('Parent cleanup function called. Current State: ' + pState);
    }
  }, [pState]);
 
  const pChangeHandler = () => {
    setPState(prevP => !prevP)
  }
 
  console.log('Parent component rendered. ' + pState);
  return (
    <div className="App">
      <button onClick={pChangeHandler}>Change P State</button>
    </div>
  );
}
 
export default App;

点击按钮1次后,控制台前2条日志为页面首次加载的输出,点击后日志表现为:组件重渲染打印的pState为true,新执行的effect打印的pState也为true,但effect清理函数打印的pState为false,和“清理函数在渲染后执行、应该拿到最新true值”的预期不符。

控制台输出截图

核心原因

这个行为完全符合React的设计逻辑,本质是JavaScript闭包特性 + useEffect固定执行时序共同作用的结果:

  • 首先要明确一个基础规则:React函数组件每一次渲染都是独立的快照,每一轮渲染生成的状态值、回调函数、effect逻辑,都会通过闭包固定捕获当前渲染轮次的所有值,不会受后续状态更新的影响。
  • 依赖变化触发重渲染时,useEffect的执行顺序是固定的:
    1. 首次加载阶段:pState初始值为false,组件首次渲染打印Parent component rendered. false;渲染完成后执行本轮注册的effect,打印Use effect parent. false,同时将该effect返回的清理函数缓存,这个清理函数从创建时就固定捕获了首次渲染的pState=false值。
    2. 点击按钮触发更新:pState被修改为true,组件基于新状态重渲染,这一轮渲染的快照里pState固定为true,因此打印Parent component rendered. true,同时生成捕获了pState=true的新effect回调。
    3. 副作用执行阶段:浏览器完成页面绘制后,React不会直接运行新生成的effect,会优先执行上一轮缓存的清理函数。这个清理函数是首次渲染阶段生成的,闭包里存储的永远是当时的pState=false,因此打印出来的值为false。
    4. 上一轮清理逻辑执行完毕后,React才会运行本轮新生成的effect,打印Use effect parent. true,同时将本轮的清理函数缓存,等待下一次依赖更新或组件卸载时执行。

认知纠偏:effect清理函数不是重渲染完成后才创建的,它是上一轮effect执行时就已经生成的闭包,设计目标就是清理上一轮effect产生的副作用,捕获上一轮渲染的状态值是完全符合预期的行为,而非bug。


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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.27 14:24:26