react 新手关于 react useEffect 的困惑,为什么 useEffect 里面的 cleanup 函数里面的 props 是旧的,如何从源码解释?

3 天前
 ooo4

版本: react@18.3.1

我看源码就是先 UnmountEffects 后 MountEffects,里面也只是递归遍历而已,为什么 cleanup 里面的 props 是上一次的了?

commitPassiveUnmountEffects(root.current);
commitPassiveMountEffects(root, root.current, lanes, transitions);
// 复现的 demo
function App() {
  const [num, setNum] = useState(100)
  window.__setNum = setNum
  return <Comp num={num}></Comp>
}

function Comp(props) {
  debugger
  useEffect(() => {
    debugger
    props // {num:1000}
    return () => {
      debugger
      // 为什么这里是旧的 props? {num:100}
      props
    }
  }, [props.num])
  return (
    <p>
      <span>{props.num}</span>
    </p>
  )
}

setTimeout(() => {
  __setNum(1000)
}, 1000)
2033 次点击
所在节点    React
47 条回复
shintendo
3 天前
闭包
lisongeee
3 天前
新状态组件的 useEffect 和旧状态组件的 cleanup 同时被你 debugger 到了
ooo4
3 天前
@shintendo 从 chrome 的 debugger 工具看,这个 props 确实是来源于闭包,thanks!!
ltaoo1o
3 天前
我这里也有一个闭包的问题,https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-kvt3jp?file=%2Fsrc%2FApp.js

react 的函数组件让人恼火,各种潜在的问题
Razio
3 天前
@ltaoo1o #4 你比 OP 还新手
ltaoo1o
3 天前
@Razio 可以详细说说吗,不要只输出态度..
Torpedo
3 天前
@ltaoo1o #4 就是闭包的问题。每次组件状态变化,函数都会执行一次。所以最新的状态都在最新的一次执行里。但是这里你 keydown 监听的是第一次函数运行的函数。那个函数的闭包上下文里,state 是最初的。

新手不要直接用 useEffect 。找个 react-use ahook 啥的。用封装好的 hook 。无论什么水平,写的好的话,需要手写 useEffect 的很少
shintendo
3 天前
@ltaoo1o 你这个我觉得无解,可行的解法你自己注释里都写了
或者干脆就从 dom 里拿 v
hyh0u0
3 天前
(变量)代码在写下来的时候就被捕获了。或者说,在这个函数的生命周期里,函数内部的那个 props 就只有传入的 props
TWorldIsNButThis
3 天前
@ltaoo1o 注释是啥意思,正确方式就是 ref 或者让 effect 依赖 v 啊
ltaoo1o
3 天前
当时出问题的时候,我就意识到是闭包问题了,百分之九十九函数组件的问题就是闭包问题 🤣

我这个代码,从语义上来说非常简单也很实际,「页面初始化后监听回车事件」,用函数组件就是写不出来,用类组件就没有这种问题。
所以我现在写代码都少用 hook ,它改变了代码作为领域知识的意义,变成为框架去改变业务含义,导致现在都是在写框架代码,不是写业务代码。
ltaoo1o
3 天前
@TWorldIsNButThis 我希望代码表达正确的「语义」
ltaoo1o
3 天前
@shintendo #8 是的,我想了很久,无解
ltaoo1o
3 天前
@Torpedo 感谢 🙏🏻
shintendo
3 天前
@ltaoo1o 你说 v 不能放 useRef 里,那可以把 log 放 useRef 里
https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-forked-pn425w
TWorldIsNButThis
3 天前
@ltaoo1o
class 组件可以是因为 this 就是 ref 啊
ltaoo1o
3 天前
@shintendo #15 起作用的是这个刷新 log 函数的代码吧,而且 log 可能是一个比较复杂的函数,它要拿很多个状态进行处理,这里就要依赖 v1 v2 v3 等等,容易漏

```
useEffect(() => {
logRef.current = () => {
alert(v);
};
}, [v]);
```
ltaoo1o
3 天前
@TWorldIsNButThis #16 所以我说函数组件有额外的心智负担,容易出问题,感觉在和框架斗智斗勇 🤣
ljpCN
3 天前
@ltaoo1o 你应该在 input 标签的 onKeyDown 回调里处理你的键盘监听,而不是在 useEffect 里处理
ltaoo1o
3 天前
@ljpCN 实现方式其实很多种,难的是保留语义的前提下。我希望我的代码,别人一看,就能明白是「当页面加载后,监听回车事件并 xxx 」,如果写在 input 标签,就变成了「当这个 input 回车时,xxx 」,这里的语义就丢失了。
当然代码能跑就行,「代码表达语义」仅仅是我个人的追求。

这是一个专为移动设备优化的页面(即为了让你能够在 Google 搜索结果里秒开这个页面),如果你希望参与 V2EX 社区的讨论,你可以继续到 V2EX 上打开本讨论主题的完整版本。

https://www.v2ex.com/t/1102355

V2EX 是创意工作者们的社区,是一个分享自己正在做的有趣事物、交流想法,可以遇见新朋友甚至新机会的地方。

V2EX is a community of developers, designers and creative people.

© 2021 V2EX