我看源码就是先 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)
1
shintendo 2 天前 1
闭包
|
2
lisongeee 2 天前
新状态组件的 useEffect 和旧状态组件的 cleanup 同时被你 debugger 到了
|
4
ltaoo1o 2 天前
我这里也有一个闭包的问题,https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-kvt3jp?file=%2Fsrc%2FApp.js
react 的函数组件让人恼火,各种潜在的问题 |
7
Torpedo 1 天前
@ltaoo1o #4 就是闭包的问题。每次组件状态变化,函数都会执行一次。所以最新的状态都在最新的一次执行里。但是这里你 keydown 监听的是第一次函数运行的函数。那个函数的闭包上下文里,state 是最初的。
新手不要直接用 useEffect 。找个 react-use ahook 啥的。用封装好的 hook 。无论什么水平,写的好的话,需要手写 useEffect 的很少 |
9
hyh0u0 1 天前
(变量)代码在写下来的时候就被捕获了。或者说,在这个函数的生命周期里,函数内部的那个 props 就只有传入的 props
|
10
TWorldIsNButThis 1 天前 via iPhone
@ltaoo1o 注释是啥意思,正确方式就是 ref 或者让 effect 依赖 v 啊
|
11
ltaoo1o 1 天前
当时出问题的时候,我就意识到是闭包问题了,百分之九十九函数组件的问题就是闭包问题 🤣
我这个代码,从语义上来说非常简单也很实际,「页面初始化后监听回车事件」,用函数组件就是写不出来,用类组件就没有这种问题。 所以我现在写代码都少用 hook ,它改变了代码作为领域知识的意义,变成为框架去改变业务含义,导致现在都是在写框架代码,不是写业务代码。 |
12
ltaoo1o 1 天前
@TWorldIsNButThis 我希望代码表达正确的「语义」
|
15
shintendo 1 天前
@ltaoo1o 你说 v 不能放 useRef 里,那可以把 log 放 useRef 里
https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-forked-pn425w |
16
TWorldIsNButThis 1 天前 via iPhone
@ltaoo1o
class 组件可以是因为 this 就是 ref 啊 |
17
ltaoo1o 1 天前
@shintendo #15 起作用的是这个刷新 log 函数的代码吧,而且 log 可能是一个比较复杂的函数,它要拿很多个状态进行处理,这里就要依赖 v1 v2 v3 等等,容易漏
``` useEffect(() => { logRef.current = () => { alert(v); }; }, [v]); ``` |
18
ltaoo1o 1 天前
@TWorldIsNButThis #16 所以我说函数组件有额外的心智负担,容易出问题,感觉在和框架斗智斗勇 🤣
|
20
ltaoo1o 1 天前
@ljpCN 实现方式其实很多种,难的是保留语义的前提下。我希望我的代码,别人一看,就能明白是「当页面加载后,监听回车事件并 xxx 」,如果写在 input 标签,就变成了「当这个 input 回车时,xxx 」,这里的语义就丢失了。
当然代码能跑就行,「代码表达语义」仅仅是我个人的追求。 |
21
ljpCN 1 天前 1
@ltaoo1o 那给你看看我改完的代码吧,个人觉得比你的语义更清晰。https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-forked-6ljjys
|
22
ltaoo1o 1 天前
@ljpCN #21 额,语义是指业务逻辑,就比如我必须表达出「当页面加载后,监听回车」,在这个前提下,如何完成需求,你的代码非常好,没有问题。只是少了「当页面加载后」的这个含义。
|
23
ljpCN 1 天前
@ltaoo1o 如果你是想要在整个页面监听回车按键,先不讨论这个需求的合理性,为了实现你要的语义清晰,你应该寻求对 hooks 的封装来实现你的 log 函数拿到最新的 state ,或者直接通过 ref 获取 input 标签当前的 value 。前者的话举一个例子: https://ahooks.js.org/hooks/use-memoized-fn
|
27
ljpCN 1 天前
|
28
LOWINC 1 天前
你这个前提就有问题
“1. 该代码表示「当页面初始化后」,所以这里不能依赖 log 、handleKeyDown 、” 可以看下 dan 的文章 https://overreacted.io/a-complete-guide-to-useeffect/ @ltaoo1o |
29
ltaoo1o 1 天前
@LOWINC 我理解你的意思,useEffecet(fn, []) 不能表达「当页面初始化后」,和 componentDidMount 不同,这也是我无语的一点,函数组件没有一个明确的函数、方式来表达「当页面、组件初始化后」。
现在社区普遍都将 useEffecet(fn, []) 作为「当页面初始化」的含义来用不是吗,如果不这么写,可以给我一个方案吗,我确实不懂该如何写 |
30
dango33 1 天前
@ltaoo1o #4 https://zh-hans.react.dev/learn/separating-events-from-effects
建议重新看一遍 React 的教程,把有些重要问题解释得很清楚了。 |
31
ltaoo1o 1 天前
@dango33 完整看完了,这个教程不是证明了我说的吗,后面提到了 useEffectEvent ,一个「还没有发布的实验性 API 」,如果我用 useEffectEvent 包 log 就能解决我的问题,为什么要发布一个新的 hook ,就是有一些场景用 useEffect 解决不了。
另外,如果希望讨论,可以把你的观点明确地表达出来,我承认自己很菜,也欢迎讨论。 |
32
dango33 22 小时 32 分钟前
@ltaoo1o #31 “建议你读教程”并不是在尝试讽刺你菜,闻道有先后,不是说只有菜鸟才看教程。如果还没有读过,建议快速过一遍,尤其是对于从 class 时代过来的人。主要是能够(在一定程度上)避免踩坑,不至于对你提出的这个问题感到恼火(因为这并不是现阶段很难发现和解决的问题)。
如果对#15 的方式不满意,也可以选择把你原来的 log 函数扔给 ref ,这样更新 ref 的 effect 就只有一个依赖了。 或者用组件外的一个实例记录要上传的数据,提供暂存和上传数据的方法给组件用,这样 useEffect 就完全没有依赖了。(一点拙见) |
33
ltaoo1o 21 小时 43 分钟前
@dango33 #32 我后面也说了,当我发现不对时,我就意识到哪里有问题,应该怎么写。但任何写法,都违背了我希望用代码表达的含义。其次,一个需要专门写文章来说明,并且还要发布新的 API 来支持更多场景的坑,不让人恼火吗,我写业务就够累了还要处处小心这种坑。
另外,我现在所有代码都是像你说的另外起实例,很少用 hook 了,像这样 ```js function HomePage() { const [state, setState] = useState($page.state); useEffect(() => { $page.onStateChange((v) => setState(v)); $page.ready(); }, []); return (); } ``` 这样写就不用考虑框架的坑了,有问题也是我自己的问题了。 |
34
zhengfan2016 21 小时 29 分钟前
第一次见到 react.fc 外面使用 settimeout ,黑马程序员也不是这么教的吧,这么写肯定容易出问题。就和不用 const 偏要用 var 一样
|
36
ooo4 OP @zhengfan2016 因为我在调试 react 源码,不想通过合成事件去触发,徒增额外调试
|
37
demonzoo 19 小时 7 分钟前
|
38
ltaoo1o 18 小时 28 分钟前
@demonzoo 哥,感谢你,可是不是我想要的 🤣 我在注释里写了不能依赖 handleKeydown 等,不然语义就变了,变成了「当 handleKeydown 」改变时,监听 keydown 事件。但是我希望表达的是「当页面加载后,监听事件」
|
39
ljpCN 3 小时 49 分钟前
@ltaoo1o 不知道你是没看到还是无视了我上面发的消息,既然你还是认为没有解决方案,我只能把解决方案写好发出来了: https://codesandbox.io/p/sandbox/zuo-yong-yu-de-wen-ti-forked-rqm5jy
|
40
ljpCN 3 小时 41 分钟前
@ltaoo1o 另外如果你真的接受了 react 文档中关于 useEffect 的定义,你应该知道 useEffect 的语义并非是 xx 发生变化时执行 yy 。useEffect 只是一个渲染过程的副作用,在严格模式下即使组件只挂载一次它也会执行两次。你不能把 useEffect 当做监听状态变化的回调来使用,而是应当作为每次渲染完成后的副作用来理解,只要你在 useEffect 的返回函数里对副作用做好适当的清理,你是不用去关心它执行了一次还是两次还是每次渲染后都执行的。当然你也可以用 useMemoizedFn 这样的 hook 来减轻你理解函数当前使用的是哪一次渲染的闭包变量的心智负担,这也完全没问题。
|