【React进阶系列第三课】组件函数什么时候被执行

前面说过,React 每次渲染组件,都会执行组件函数。现在,我们来看看,React 会在什么时候渲染组件,也就是什么时候会执行你的组件函数。

组件函数执行的 3 种情况

你的组件函数会在以下 3 种情况下执行:

  1. 组件首次挂载时
  2. 组件的状态或依赖的 Context 发生变化时
  3. 父组件重新渲染时

第 3 种情况,有必要单独说一下。当 React 更新一个组件的时候,会连带者更新它的子组件,即使子组件的 props 没有发生变化。也就是说,React 更新的是整个组件树,而不是单个组件。

有个普遍的错误说法是 "props 变化时,组件会更新"。第一,props 变化,意味着父组件更新了,此时组件更新是因为父组件更新导致的,不管 props 有没有变化,组件都会更新。其次,如果按这个理解,那你说下面这种情况组件会更新吗?

jsx 复制代码
import { createRoot } from "react-dom/client";

let sec = 0;
setInterval(() => {
  sec++;
}, 1000);

function Timer({ secPassed }) {
  return <p>Seconds passed: {secPassed}</p>;
}

createRoot(document.getElementById("root")).render(<Timer secPassed={sec} />);

性能问题

你可能发现了,父组件更新,所有的后代组件也会一起更新,那性能是不是会有问题?尤其是对那些 props 没有变化的后端组件,执行它们不是浪费吗?

从性能上看这确实是一个浪费。不过,实践证明,在大部分项目下,这种性能损耗不会导致明显的性能问题,不然 React 已经被喷惨了。所以,我们平时不用担心性能问题。

不会引发性能问题,很大一个原因是因为大部分的组件函数都很简单,很快就执行完了。一般的组件函数都是做一些变量的声明、读取、赋值,然后创建并返回 React 元素(JSX)。

这也告诉我们,尽量让组件函数简单,把一些具有较高复杂度的计算放在 useMemo 中,避免不必要的计算。

另一个需要关注的是组件的数量,比如:

  • 大量使用某个组件。比如,展示大量数据,每个数据对应一个 <ListItem />
  • 一个组件有大量的后代组件,而且它自身频繁更新。

这类情况,在每次更新时,都会执行大量的组件函数,容易导致性能问题。可以使用 React.memo,当组件的 props 没有变化时,跳过组件的更新。

React.memo

memo 可以让我们在更新前,比较新旧两次的 props,决定要不要执行更新。

js 复制代码
const MemoizedComponent = memo(SomeComponent, arePropsEqual?)

默认情况下,是对每个 prop 做浅比较,如果存在对象结构的 prop,一般使用 lodash.isEqual 来做深比较,如果含有函数,还需要注意保证先后两次函数的引用不发生变化,不然即使 lodash.isEqual 也会返回 false

memo 优化的是第 3 种情况,也就是父组件更新都会导致子组件更新的情况。对于第 1、2 种情况,组件更新是不能也不可避免的。

memo 可以提升性能,但是,不要滥用,除非你有理由认为这个组件的频繁更新会导致性能问题。

最后,应该在组件使用的时候,才决定要不要使用 memo,不要在组件定义的时候就使用 memo。假如你对外提供组件,你不需要在导出之前使用 memo。因为你写组件的时候,不知道父组件会不会频繁更新,也不知道组件会不会被大量使用,也不知道如何有效比较两次 props,这些只有使用的时候才知道。

总结

  • 函数组件会在 3 中情况下执行:
    1. 组件首次挂载时
    2. 组件的状态或依赖的 Context 发生变化时
    3. 父组件重新渲染时
  • 父组件更新时,子组件默认都会更新,即使子组件的 props 没有变化。
  • 这种冗余的更新,在大部分情况下,不会导致明显的性能问题。
  • 为了避免性能问题,记住:
    • 尽量保持组件简单。
    • 在组件内如果有高复杂度的计算,使用 useMemo
    • 对于大量使用的组件,或者更新繁重的组件,可以在必要时使用 React.memo
  • 组件自身的状态或依赖的 Context 变化时,组件一定会更新,React.memo 不会影响这种情况。
相关推荐
To_OC3 小时前
别再串行写 await 了,Promise.all 才是并行请求的正确打开方式
前端·javascript·promise
vipbic3 小时前
中后台越做越乱后,我用插件化把它救回来了
前端·vue.js
Hyyy3 小时前
Computer Use 适合做什么,不适合做什么——一次真实使用后的思考
前端
小和尚同志4 小时前
前端 AI 单元测试思考与落地
前端·人工智能·aigc
invicinble5 小时前
c端系统,其实更像一个信息展示平台
前端
李姆斯5 小时前
管理是否可以被完全量化
前端·产品经理·团队管理
名字还没想好☜6 小时前
Next.js 中间件实战:鉴权、重定向与 A/B 分流
开发语言·前端·javascript·中间件·react·next.js
广州灵眸科技有限公司6 小时前
瑞芯微RV1126B开发板(EASY-EAI-PI2) INI文件操作
java·前端·javascript·网络·人工智能
江华森7 小时前
Web 开发基础:HTTP 与 REST
前端·网络协议·http
IT_陈寒8 小时前
Redis的KEYS命令把我搞崩溃了,改用SCAN才活过来
前端·人工智能·后端