请教一个,有关 react 的 re-render 的性能小问题

2021-06-24 15:52:18 +08:00
 yazoox

有关 react 的两个小问题。

import "./styles.css";
import React, { useState } from "react";

export default function App() {
  const [num, setNum] = useState(0);

  const handleClick = () => {
    setNum(num + 1);
  };

  return (
    <React.Fragment>
      <span>{num}</span>
      <button className="button1" onClick={handleClick}>
        Click Me
      </button>
      <button className="button2" onClick={() => setNum(num + 1)}>
        Click Me Again
      </button>
      <div className="App">
        <h1>Hello CodeSandbox</h1>
        <h2>Start editing to see some magic happen!</h2>
      </div>
    </React.Fragment>
  );
}

我忘记在哪里看到的(年纪大了,记性不太好),在以前 class component 的时代,不推荐 button2 的写法,就是把函数体直接赋值给 button's onClick 。因为这样会使得每次 button2 都有变化,会 re-render 。在大的项目 /组件多的情况下,会有性能问题。一般是建议在 class 里面定义一个函数,然后赋值给 button's click.

现在来到 function component 时代,当 button 被点击的时候,会触发 function app 被重新调用,handleClick 也会被重新生成,这个时候,button1 和 button2 的用法,还有区别么?不过,当 handleClick 比较复杂时,应该可以通过 useCallback 提高性能。虽然 const handleClick 是一个新的变量,但函数体被记忆 /缓存了。

const handleClick = useCallback(() => { ... });

另,我还“隐约”记得,button1 的写法,当用户点击 button 时,会触发“两”次 render,我不记得为什么了?是我记错了么?

最后,有没有什么 debug 方法,能够知道,比如,我点击 button1 后,跟踪一下,触发了多少次 render ?

谢谢!

1902 次点击
所在节点    React
13 条回复
kop1989
2021-06-24 16:17:01 +08:00
关于渲染两次:
auroraccc
2021-06-24 16:22:29 +08:00
我理解 b1b2 没区别了
lxzxl
2021-06-24 19:07:36 +08:00
你这么写 useCallback 没用,setNum 传函数才行
lisianthus
2021-06-24 20:06:49 +08:00
>现在来到 function component 时代,当 button 被点击的时候,会触发 function app 被重新调用,handleClick 也会被重新生成,这个时候,button1 和 button2 的用法,还有区别么?
个人认为没区别 ,因为 handleClick 指向一个匿名函数,每次渲染都会生成一个新的函数引用,可以用 useCallback 来让 handleClick 指向的函数引用不变。
> 当用户点击 button 时,会触发“两”次 render,我不记得为什么了?是我记错了么?
刚在函数组件试了下,好像只触发了一次重渲染 ( React 17.0.2)
mxT52CRuqR6o5
2021-06-24 20:13:39 +08:00
回调函数缓不缓存其实性能影响不大,useCallback 主要目的是缓存传给子组件的 render function,而且性能不是 react 设计的主要考量因素,react 自己那一套哲学才是设计主要考量因素
ChefIsAwesome
2021-06-24 20:41:55 +08:00
性能不能口头说,找个老手机一测便知。
sweetcola
2021-06-24 21:13:53 +08:00
> button1 和 button2 的用法,还有区别么?
没有区别。因为一旦渲染你写在外面和 onClick 里面都一样是动态生成的函数。
不过差别也有,就是非匿名函数和匿名函数的区别(也就是没什么区别)。

> 不过,当 handleClick 比较复杂时,应该可以通过 useCallback 提高性能
如果函数不影响重渲染的情况下用不用没有区别,甚至用了还会降低效率(依赖的比较)。因为就算你用了 useCallback,在重渲染的那一刻依然会生成一个函数,只是依赖没有变所以没有替换(以我理解)。

> 跟踪一下,触发了多少次 render ?
在组件里 console.log('Render'); 就可以了。
dany813
2021-06-25 11:19:42 +08:00
简单项目,就随便 render 了,想优化渲染就,useMemo 了
CodingNaux
2021-06-25 13:08:24 +08:00
- button 1,button 2 谁好?
没区别,又不是作为 props 传给子组件

- debug
react devtool 里面就是性能分析,或者 console.log
CodingNaux
2021-06-25 13:17:47 +08:00
DrakeXiang
2021-06-25 19:43:20 +08:00
https://reactjs.org/docs/hooks-faq.html#are-hooks-slow-because-of-creating-functions-in-render
这里说之前对 inline function 的主要担心是会对子元素造成重复渲染,可以用 useCallback 这些来避免。重复创建函数并没有多大影响,不过我记得之前看到的文章说这种匿名的 inline 函数不光是会重复创建,而且因为是匿名的,创建之后只要页面存在就不会被垃圾回收,相当于内存泄漏,但是刚才搜了一下貌似都没看到这条。如果这条不成立的话那我觉得已经是没有影响的了
sheen
2021-06-26 01:15:33 +08:00
不让直接给 onClick 赋值一个匿名箭头函数的一个原因就是每次渲染都会重新生成一个新的函数,导致子组件里面的 PureComponent 或者 memo 失效了。
zhea55
2021-07-05 22:00:55 +08:00
b1 b2 完全不同,b2 每次 render 都会产生一个新的方法。

代码参加我在这个帖子里面回复的图片。

https://v2ex.com/t/787677#reply13

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

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

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

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

© 2021 V2EX