React中useEffect的return清理函数与主体逻辑执行顺序是什么?
核心执行规则(官方标准逻辑)
useEffect的执行时机严格和组件渲染生命周期绑定,不存在“所有场景都先跑清理函数再跑effect主体”的逻辑,分三个场景对应不同顺序:
- 组件首次挂载渲染完成:直接执行所有useEffect传入的回调主体,此时没有历史注册的清理函数,不会触发任何清理逻辑。
- 组件依赖项变化触发重渲染、重渲染完成后:先执行上一轮effect回调返回的清理函数,所有清理逻辑执行完毕后,再执行本轮新的effect回调主体。
- 组件从页面卸载时:只执行最后一轮effect留下的清理函数,不会再执行effect主体。
教程里提到的“清理函数先于effect主体执行”,仅适用于上面说的重渲染、卸载两个场景,没有覆盖首次挂载的情况,属于描述缺漏。
测试结果不符合预期的两个核心原因
1. 清理函数写法错误
测试代码中,effect回调的return后直接写了console.log('return'),这行代码是effect主体的同步执行内容——也就是说每次effect跑的时候,会先打印add,紧接着立刻执行这个console打印return,再把console的返回值undefined交还给React。React拿到undefined会认为这个effect没有注册清理函数,后续重渲染、卸载阶段根本不会执行预期的“清理逻辑”。
正确的清理函数写法必须返回一个包裹清理逻辑的函数,示例修正:
useEffect( () => { console.log('add') // 要返回一个函数,函数内部的逻辑才是React会调用的清理逻辑 return () => { console.log('return') } }, [val] )
2. React 18 开发环境严格模式的特殊行为
如果使用React18及以上版本,脚手架默认会开启<StrictMode>严格模式。开发环境下严格模式会在组件首次挂载时,主动模拟一次「执行effect → 执行清理 → 重新执行effect」的流程,用来检测开发者是否遗漏了副作用清理逻辑,这个双调用行为只在开发环境出现,生产环境不会触发。
结合写错的清理函数逻辑,首次加载时严格模式触发两次effect执行,每次effect同步打印add+return,就得到了add -> return -> add -> return的日志顺序,这个结果不能用来验证正常的useEffect执行逻辑。
修正代码后的正常执行顺序参考
把清理函数写法改对、暂时关闭严格模式排除开发环境干扰后,操作对应的日志顺序如下:
- 页面首次加载完成:打印
add - 第一次输入触发val更新、重渲染完成:先打印
return(执行上一轮的清理函数),再打印add(执行本轮effect主体) - 第二次输入触发val更新、重渲染完成:先打印
return,再打印add - 组件从页面卸载:最后打印一次
return
内容的提问来源于stack exchange,提问作者Data T

