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的执行顺序是固定的:
- 首次加载阶段:
pState初始值为false,组件首次渲染打印Parent component rendered. false;渲染完成后执行本轮注册的effect,打印Use effect parent. false,同时将该effect返回的清理函数缓存,这个清理函数从创建时就固定捕获了首次渲染的pState=false值。 - 点击按钮触发更新:
pState被修改为true,组件基于新状态重渲染,这一轮渲染的快照里pState固定为true,因此打印Parent component rendered. true,同时生成捕获了pState=true的新effect回调。 - 副作用执行阶段:浏览器完成页面绘制后,React不会直接运行新生成的effect,会优先执行上一轮缓存的清理函数。这个清理函数是首次渲染阶段生成的,闭包里存储的永远是当时的
pState=false,因此打印出来的值为false。 - 上一轮清理逻辑执行完毕后,React才会运行本轮新生成的effect,打印
Use effect parent. true,同时将本轮的清理函数缓存,等待下一次依赖更新或组件卸载时执行。
- 首次加载阶段:
认知纠偏:effect清理函数不是重渲染完成后才创建的,它是上一轮effect执行时就已经生成的闭包,设计目标就是清理上一轮effect产生的副作用,捕获上一轮渲染的状态值是完全符合预期的行为,而非bug。
内容的提问来源于stack exchange,提问作者user123456789
相关产品推荐
相关产品推荐

