componentWillUpdate
一、认识
componentWillUpdate
是 React
类组件中旧的生命周期方法之一,它在组件接收到新的 props
或 state
后、但在调用 render
方法之前执行。componentWillUpdate
可以使用新的生命周期方法 getSnapshotBeforeUpdate
来代替。
React Fiber
架构实现了 Fiber
将渲染模式从原来 不可中断的同步任务改为可中断的异步任务, 任务单元之间的任意切换和任务之间的打断和恢复等等。Fiber
架构下的异步渲染, 导致了 componentWillMount
、componentWillReceiveProps
、 componentWillUpdate
三个生命周期在实际渲染之前可能会被调用多次,产生不可预料的调用结果。因此这三个生命周期函数不建议被使用,取而代之的是使用全新的两个生命周期函数 getDerivedStateFromProps
和 getSnapshotBeforeUpdate
React Fiber
架构下, 基于 Schedule
调度之后, 开始进行 Render
调和 和 Commit
提交。Render
调和阶段 的主要目标是生成一棵新的 Fiber
树,并为后续的 DOM
更新准备好 副作用列表(effect list
)。这个阶段不直接操作 DOM
,而是进行计算和比较。render
阶段以 DFS
深度优先 的顺序遍历 ReactElement
, 如果有子节点, 遍历子节点, 如果没有子节点遍历兄弟节点。Render
阶段 可以中断和恢复。Commit
提交阶段 是 React
更新流程中唯一会直接操作 DOM
的阶段,它根据 Render
阶段生成的副作用列表执行实际的更新操作。这个阶段是同步执行的,确保所有副作用在一次提交中完成, 这个阶段是同步且不可中断的,确保 UI
的最终状态与最新的 React
元素描述完全一致。简单来说,由于 Render
阶段的操作对用户来说其实是不可见的,所以就算打断再重启,对用户来说也是零感知。而 commit
阶段的操作则涉及真实 DOM
的渲染,所以这个过程必须用同步渲染来求稳。
由于 Render
阶段 可以中断和恢复, 因此, 这就导致 render
阶段的生命周期都是有可能被重复执行的。componentWillMount
、componentWillReceiveProps
和 componentWillUpdate
都是在渲染阶段调用的,而不是在提交阶段调用。因此,如果渲染过程被中断或重启,这些方法就有可能被执行多次。由于这些方法会重复调用,如果其中包含了副作用操作(例如直接修改组件状态、调用 API
等),就可能导致意外的行为或性能问题。这也是为什么 React
官方建议尽量避免使用这些生命周期方法,并将它们标记为“不安全”(UNSAFE_
)。
那么, 为什么 getDerivedStateFromProps
和 getSnapshotBeforeUpdate
可以呢?
-
getDerivedStateFromProps
, 它在Render
阶段调用,每次组件更新(包括挂载和更新)时都会在render
之前被调用,用来根据新的props
计算并返回新的state
。它是一个静态方法,不依赖于组件实例,因此无法直接访问或修改实例属性,也就不会产生副作用。它根据当前props
和state
计算出新的state
,只是返回一个对象供React
合并,完全不会触发额外的副作用。即使在并发模式下render
可能会被中断或重复调用,由于它是纯函数,多次调用不会对组件产生不一致的影响。 -
getSnapshotBeforeUpdate
, 在render
之后、DOM
更新之前调用,用于捕获(snapshot
)一些即将更新的DOM
信息。捕获的信息会传递给componentDidUpdate
,以便在DOM
更新后使用。它同样不应引入副作用,只用于读取数据。它只读取并返回数据,而不会直接引入副作用, 没有引起状态或数据的直接修改。由于其仅用于 读取 操作,多次调用也不会对最终结果产生负面影响。