React.useEffect 的效果,为什么还跟帧率有关? bug 到底出在哪里?

2022-11-10 12:56:44 +08:00
 sillydaddy

下面是最小 demo ,逻辑很简单:count 随着鼠标滚轮滚动,不断增加++,通过{divs}渲染出 count 。

const useWheel = (count:number, setCount)=>{
    let refDiv = useRef<HTMLDivElement>(null);
    useEffect(()=>{
        let el = refDiv.current;
        let callback = (ev:WheelEvent)=>{
            ev.preventDefault();
            setCount(count+Math.floor(Math.abs(ev.deltaY/5)));
        };
        el.addEventListener("wheel", callback, {passive:false});
        return (()=>{el.removeEventListener('wheel', callback);});
    }, [count]);
    return refDiv;
}

export const Main = (props)=>{
    let [count, setCount] = useState<number>(0);
    let wheel = useWheel(count, setCount);
    let divs = (new Array<number>(25000)).fill(0).map((v,i)=>(<div key={i+count}>{i+count}</div>));
    return (<div ref={wheel}>
                {divs}
            </div> );
}

但实际上,渲染的 count 会有「忽增忽减」的现象,感觉不正常。而如果把 25000 条改为 50 条,渲染帧率提高,就不会有同样的问题了。

上面的例子里,useEffect 是依赖 count 的,按我的理解,count 更新会触发 useEffect 调用。而 useEffect 里面用到的 count 就是最新的值,那为什么结果会「忽增忽减」呢?

修改 setCount 为函数形式,好像就解决这个问题了。

setCount((pre)=>pre+Math.floor(Math.abs(ev.deltaY/5)));

但我不理解这里的逻辑:为什么改之前的会有问题呢?难道 useEffect 用的不是最新的 count 吗?

4074 次点击
所在节点    React
54 条回复
ragnaroks
2022-11-11 14:16:29 +08:00
@sillydaddy
好强的攻击性,不知道我这里怎么让你误解了什么东西,不过我还是说清楚。

SOF 是 stackoverflow ,一个全球知名技术问答站点,“面向 SOF 编程”是程序员的梗,我这里特地说明不是开玩笑,因为你历史记录里面遇到的问题在 SOF 上都能找到优质解答,而且绝大多编码大佬包括林纳斯都在 SOF 上回答别人的问题。

百度: https://www.baidu.com/s?ie=UTF-8&wd=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B

谷歌: https://www.google.com/search?q=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B

必应: https://cn.bing.com/search?q=%E9%9D%A2%E5%90%91+stackoverflow+%E7%BC%96%E7%A8%8B
ragnaroks
2022-11-11 14:17:53 +08:00
@sillydaddy 如果最终是你误解我了,我乐意接受你的道歉;如果确实是我惹怒你了,我可以给你道歉。
sillydaddy
2022-11-11 14:20:44 +08:00
@ragnaroks
我觉得你需要练习一下怎么说话,尤其是在网上。真的,不开玩笑。另外请不要再污染这个帖子,拜托🙏,从你一开始回帖就是在讨论与主题无关的。至于我是否误解你,我觉得倒不是那么重要,别人自有看法。
ragnaroks
2022-11-11 14:35:06 +08:00
@sillydaddy 我重新审视,确实如你所说,你想知道的是 useEffect 在这为什么没有按照你的预想工作,有效回复就是给出 react 官方文档即可,我的第一条回复是给出了一个正常工作的样例代码,对你没有帮助。至于我是否需要练习一下怎么说话,尤其是在网上,我觉得倒不是那么重要,别人自有看法。这是我在你的贴子里最后一条回复,不会再污染你的任何贴子。也请你点进我的个人资料再点 block ,因为作为 react 贡献者我或多或少会在 react 节点中别人的贴子里再次出现。
ruxuan1306
2022-11-11 14:41:59 +08:00
@ragnaroks 只面向 StackOverflow 的话永远不知其所以然,永远外包水平。

@sillydaddy 学习方法是对的,我是读了那个长长的博客里的很多篇才明白的。

我认为关键是要搞清 React 一帧一帧渲染的思想,每帧渲染(函数组件执行返回),且上屏后(协调完成),才按顺序执行每帧各自的 Effect ,值得注意的是,这些 Effect 仍然处在其所属帧的闭包里。
chenjiangui998
2022-11-11 15:12:16 +08:00
居然扯了一大堆,还用到➕锁的, 这代码死循环了, 监听自身改变, 又自增, 这不扯淡吗
Envov
2022-11-11 16:56:52 +08:00
@chenjiangui998 这个代码虽然有问题,但是没有死循环
Envov
2022-11-11 18:04:29 +08:00
@chenjiangui998 https://codesandbox.io/s/divine-framework-28xq8s?file=/src/App.tsx
这里有一个监听自身改变, 又自增的例子,和 OP 这个类似,useeffect 递归调用是没问题的,但是也会乱序
sillydaddy
2022-11-11 19:30:38 +08:00
@ragnaroks

对于#36 楼的回复,即使你没有看众多的评论,如果阅读了主题的话,也会知道我已经知道了这个 pre=>pre+1 的方法,而且我问的也不是这个问题。直接贴个这样的回复,即使不被认为无礼,也是毫无意义的。

对于#37 楼的回复,提到了“翻了最近的疑问”,我想请问真的读过吗?还是只看了题目?上个 React 的帖子里,你的回复仍然是驴头不对马嘴。至于后面的建议我实在不理解,这个建议成立的前提是,我没有看过文档,也没有去编码实践,可是如果你翻过之前的帖子内容,怎么会不知道我是在做真实的项目?我甚至直接提到了「我的项目中」。你又凭什么推测我没有看文档呢?

对于#39 楼的回复,我也搞不懂,你连这个主题和其他主题的内容都没有看,怎么知道我没有搜索过 stackoverflow ?你如果看了主题内容,就知道这些都不能在 stackoverflow 直接搜索出来。不说其他主题,就这个主题的内容,你给找出来一个 stackoverflow 的例子?另外据我所知,stackoverflow 中,也从来没有直接说你去读文档多练练代码的,至少要指出问题所在的点,给出文档对应的知识点,这才是有礼节有意义的回复吧。

别人初看你的评论似乎很友好,可能还要说我刻薄。但通过上面的观察,你连题目都没看,根本没想着好好交流,而是一直在以居高临下的姿态在说教。你充满优越感不要紧,但不要贬低别人,不要以为说的话里有「建议」「可能」就是友好的。这也是我反感你的原因。

#41 楼 #44 楼的回复我就不评论了。只能更加印证我的观点。
sillydaddy
2022-11-11 19:47:20 +08:00
@ragnaroks 对于你在#44 楼有意无意提到的 react 贡献者,以你在这个帖子里表现,如果是真的话,反而显得更加讽刺。我觉得其他楼层的人**都**是你的榜样,无一例外。
sillydaddy
2022-11-14 10:57:38 +08:00
对于楼上的公案,这个 @ragnaroks 在另外一个帖子里 /t/894861 里又发表了下面的评论,可以理解,毕竟已经说出大话再也不给我回帖了,再不回帖就要给憋坏了。

他的评论:
```
这个其实简单来说就是“我发帖是来找认同的,不是来找质疑”的。

比如我的最近回复 /t/894109 ,我先入为主发了我觉得楼主能看懂的代码,对比其区别答案就显而易见,但是那个楼主其实看不懂,我又说面向 SOF 编程就行,被他理解为在教他做人,那我只能顺着他的意思给他道歉,辛亏没机会在现实共事。

不过说回来,在网络上发言确实很多情况下没有考虑到别人的需要,很多时候是“我觉得你需要这个”或“不管你现在怎么想,听我的没错”。我自认为我就是这样的人,先入为主认为别人应该和我具有相同水平,能完全正确理解我的意思,不过我是不会改的,这是我的个性,不管是好是坏。

实际上在网络上很多人中文都没法正确理解,比如把和“学力”看成“学历”。随着冲突加剧,网络论坛素质变低是无法避免的。以前有个笑话是只有黄网下面回复都是楼主好人,实际上 P 站评论区人种歧视起步。
```

当然,贴出这个并不是为了反驳或羞辱他,而是因为他的新评论明显就是混淆黑白了(除非他的水平真的不懂我楼上的回复在说什么)。对于这种混淆黑白,我是一定会给予回击的,不要以为随便颠倒是非可以不受惩罚。
morelearn1990
2022-11-14 16:30:56 +08:00
@sillydaddy 简单的可以理解为发布订阅模式。个人感觉 vue3 要比 react 相对较晚遇到性能瓶颈吧,react 一个小小的组件就有可能因为输入事件需要解决 useEffect 或者 useCallback 重复申明导致的性能问题。vue3 的 setup 只运行一次,其实更加符合直觉点。不过不同的框架都有不同框架的烦恼,vue3 跨不过去 ref 的坎,写起来挺麻烦的。
sillydaddy
2022-11-15 14:36:31 +08:00
@ragnaroks
今天又翻了翻你最近的回复记录,发现你的回复都很友好(虽然最近能从偶尔几个回复中感觉到一点变化)。我感觉前几天确实误解你的意思了。我向你道歉。
误解的其中一个原因是上次翻看你的回复记录,恰好碰到你正在帖子 /t/894008 里跟其他人争论,我翻到那儿就没有继续往后翻了(因为感觉找到证据了)。
跟人吵架对人心理影响是比较大的,起码对于我自己是这样。希望没有对你造成太大困扰,sorry 。
realJamespond
2023-03-02 18:01:31 +08:00
滚动后不停的 listen 又不停 remove 的意义是啥?

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

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

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

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

© 2021 V2EX