【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 不会影响这种情况。
相关推荐
~无忧花开~25 分钟前
React生命周期全解析
开发语言·前端·javascript·react.js·前端框架·react
哈__43 分钟前
ReactNative项目OpenHarmony三方库集成实战:react-native-maps
javascript·react native·react.js
cj814044 分钟前
Prompt,Agent,Skill,Mcp分别于langchain有什么关系
前端
SuperEugene1 小时前
Axios + Vue 错误处理规范:中后台项目实战,统一捕获系统 / 业务 / 接口异常|API 与异步请求规范篇
前端·javascript·vue.js·前端框架·axios
行走的陀螺仪1 小时前
手写 Vue3 极简 i18n
前端·javascript·vue.js·国际化·i18n
羽沢311 小时前
一篇简单的STOMP教程QAQ
前端·javascript·stomp
code_Bo1 小时前
使用AI完成Swagger接口类型在前端自动生成的工具
前端·后端·架构
加个鸡腿儿2 小时前
从"包裹器"到"确认按钮"——一个组件的三次重构
前端·vue.js·设计模式
子兮曰2 小时前
AI写代码坑了90%程序员!这5个致命bug,上线就炸(附避坑清单)
前端·javascript·后端
猪八宅百炼成仙2 小时前
PanelSplitter 组件:前端左右布局宽度调整的实用解决方案
前端