一个关于 react 函数组件重新渲染的问题

2021-04-19 21:11:43 +08:00
 wiluxy

点击按钮后,触发 setCount(0+1), 此时 count 改变,改变后 count 为 1,函数重新渲染 输出"render" 然后再次点击按钮,触发 count+1,但是外面包了一层 useCallback,函数里面的 count 应该是第一次渲染时的 count,也就是 setCount(0+1),第二次点击 connt 是没有变的,但是函数还是重新渲染了,输出了"render",这是为什么呢?

代码如下

import React, { useCallback,  useState } from "react";

const App = () => {

  console.log("render");
  
  const [count, setCount] = useState(0);

  const Add = useCallback(() => {
    setCount(count + 1);
  }, []);

  return (
    <div>
      {count}
      <button onClick={Add}>add count</button>
    </div>
  );
};

export default App;
3177 次点击
所在节点    React
25 条回复
mxT52CRuqR6o5
2021-04-19 21:17:35 +08:00
ericls
2021-04-19 21:20:49 +08:00
不管 count 变没变 你 dispatch 了就是 dispatch 了
huijiewei
2021-04-19 21:28:59 +08:00
函数组件就是这样的,里面任何状态的变化都会重新运行函数 :)
wiluxy
2021-04-19 21:32:01 +08:00
djyde
2021-04-19 21:43:29 +08:00
还有另外一个问题是 setCount(count + 1); 在 callback 里总是初始的 count, 应该用 setCount(count => count + 1)
wiluxy
2021-04-19 22:11:46 +08:00
@djyde 这里是为了让每次点击的时候 setCount 的值是一样才这么做的,setState 的时候会用 Object.is 比较新旧值,一样的话好像就不重新渲染页面了
aaronlam
2021-04-19 22:54:54 +08:00
只要调用了 useState 所返回数组里的第二个函数元素,就会重新执行函数组件
wiluxy
2021-04-19 23:04:54 +08:00
@aaronlam 这个例子里面只有前两次点击 add count 会输出“render”,后面再怎么点也不会输出 render 了,count 的值也没有变化
zhuangzhuang1988
2021-04-19 23:33:36 +08:00
所以 react hook 反人类。。。
imjamespond2020
2021-04-19 23:50:27 +08:00
usecallback 的用法是传递到子组件的配合 useeffect 用的。。
weimo383
2021-04-19 23:50:38 +08:00
你的 add 函数是一个在 mount 阶段就被缓存的函数,不会重新创建,生成闭包
weimo383
2021-04-19 23:53:27 +08:00
函数执行并不代表组件的重新渲染。实际上 react 组件返回的是新的虚拟 dom
shzx1994529
2021-04-20 01:06:08 +08:00
函数执行不等于组件渲染,后面可能会被 diff 掉,不用太在意
ericgui
2021-04-20 09:25:26 +08:00
setCount(count => count + 1);
dany813
2021-04-20 10:38:00 +08:00
@shzx1994529 diff 多了 也挺费劲的
shzx1994529
2021-04-20 11:45:45 +08:00
@dany813 也是,不过正常开发不乱写一般没啥性能问题
wiluxy
2021-04-20 11:47:58 +08:00
@ericgui 并不是要 setCount 能更新,而是 setCount 相同的值能触发函数更新 ,count 为 1 的时候 setCount(1),能触发更重新运行函数组件,但是后续触发又不触发了
liuqiongyu889
2021-04-20 13:37:39 +08:00
你的依赖有问题,应该写为:
const Add = useCallback(() => {
setCount(count + 1);
}, [count]);

不然 count 没有更新,还是原来的引用,可以看下这篇文章,解释 useCallback 和 useMemo 作用,为什么需要这个东东:

[一句话解释 useCallback 与 useMemo 的区别 & 作用]( https://markdowner.net/article/153901518641561600)
liuqiongyu889
2021-04-20 13:39:58 +08:00
如果你不想在依赖列表中写 count,应该参考 @ericgui 的写法,传递一个函数进去:
setCount(count => count + 1);
wiluxy
2021-04-20 13:43:51 +08:00
@liuqiongyu889 这里是为了复现这个问题才这样写的,故意让 count 是第一次运行时的值( 0 ),第一次点击的时候 count 0->1,第二次点击的时候 count 1->1,理应是不会触发 App 函数重新运行的,结果触发了,但是第三次点击的时候 count 1->1,但是又没有触发运行,疑问点在这,不是 useCallback 的疑问,而是 useState,更新状态函数的疑问。

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

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

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

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

© 2021 V2EX