我想知道 React 组件的生命周期方法componentDidUpdate
是在所有子级render
方法完成后执行,还是在render
调用该组件的方法之后立即执行。
由于协调器递归调用render
方法来更新视图,我有一种预感,它componentDidUpdate
会在重新渲染组件的所有子组件后执行,但文档中没有足够的信息。具体什么时候componentDidUpdate
调用?
我想知道 React 组件的生命周期方法componentDidUpdate
是在所有子级render
方法完成后执行,还是在render
调用该组件的方法之后立即执行。
由于协调器递归调用render
方法来更新视图,我有一种预感,它componentDidUpdate
会在重新渲染组件的所有子组件后执行,但文档中没有足够的信息。具体什么时候componentDidUpdate
调用?
该componentDidUpdate
方法render
在组件的方法执行完成后被调用。这意味着它将在所有子级render
方法完成后调用。您链接的文档中暗示了这一点:
以此为契机,在组件更新后对 DOM 进行操作。
该组件仅在渲染后更新,因此文档暗示它在所有子级以及父级完成重新渲染后调用(尽管有点不清楚)。只有当 DOM 完成更新后,您才能真正对 DOM 进行操作,子项等等。
例如,假设我们有两个组成部分,A
并B
与B
渲染A
组件。componentDidUpdate
forB
只会被调用一次B
的render
完成。在render
中B
后会完成A
的render
调用成功,因为孩子们第一次呈现,由于父母是一部分。这意味着您的问题的答案是:componentDidUpdate
在所有孩子的render
s 完成后执行。
不确定某处是否有更深入的文档,但您自己测试确实很容易。
class Nested extends React.Component {
constructor(props){
super(props);
this.state = {foo: undefined};
}
render() {
console.log("Rendered " + this.props.name);
return <div><button onClick={() => {this.setState({foo: Date.now()})}}>Update {this.props.name}</button>{this.props.children ? React.cloneElement(React.Children.only(this.props.children), {foo: this.state.foo}) : undefined}</div>
}
componentDidUpdate() {
console.log("Updated " + this.props.name);
}
}
ReactDOM.render(<Nested name="outer"><Nested name="inner"></Nested></Nested>, document.getElementById("app"));
http://jsbin.com/yiyuhegayo/edit?js,console,output
更新外部组件会导致componentDidUpdate
首先运行最里面的组件,然后是最外面的组件。更新内部组件只会导致该组件更新。
有趣的是,render
功能正好相反。外部组件首先渲染,然后是内部组件。
正如@Andrew Li 正确指出的那样,componentDidUpdate
在渲染所有子项后执行。虽然这仍然存在,但从那时起,React 的 Fiber 协调算法(版本 16.0 以后)可能已经改变了以前的假设。
简而言之,React 将工作分为两个阶段,render
(先执行)阶段和commit
(之后执行render
)阶段。componentDidUpdate
属于该commit
阶段,并且是该阶段中调用的最后一个生命周期方法(在getSnapshotBeforeUpdate
、componentWillUnmount
和 之后componentDidMount
),因此将始终在渲染节点及其子节点之后发生。
该render
阶段以“深度优先”的方式遍历 Fiber 树,如下图所示(摘自强烈推荐的Inside Fiber:React 中新协调算法的深入概述)。
(子节点水平显示,因此c1
是 的子节点b2
和b3
是 的兄弟节点b2
,而b1
没有任何子节点。)
此外,组件可能会重新渲染(例如,在状态更改、HOC、连接的props等之后)并且这不会触发componentDidUpdate
父级上的a 。我认为情况总是如此,甚至在 v16.0 之前也是如此。
综上所述,我认为对子组件的渲染做出假设是不寻常的(并且可能是不安全的),并且会在这些组件之间造成不必要的耦合,并建议每个组件都足够module化,不依赖于其父组件componentDidUpdate
或子组件render()
. 如果您需要通知父子(ren)已完成渲染,您可以将函数作为 prop 传递,然后在componentDidUpdate
or componentDidMount
(或useEffect
)中调用该函数。