componentDidUpdate 是否在所有子项都更新后运行?

IT技术 reactjs
2021-04-25 05:57:31

我想知道 React 组件的生命周期方法componentDidUpdate是在所有子级render方法完成后执行,还是在render调用该组件方法之后立即执行

由于协调器递归调用render方法来更新视图,我有一种预感,它componentDidUpdate会在重新渲染组件的所有子组件后执行,但文档中没有足够的信息具体什么时候componentDidUpdate调用?

3个回答

componentDidUpdate方法render在组件方法执行完成后被调用这意味着它将在所有子级render方法完成后调用您链接的文档中暗示了这一点:

以此为契机,在组件更新后对 DOM 进行操作

该组件仅在渲染后更新,因此文档暗示它在所有子级以及父级完成重新渲染后调用(尽管有点不清楚)。只有当 DOM 完成更新后,您才能真正对 DOM 进行操作,子项等等。

例如,假设我们有两个组成部分,ABB渲染A组件。componentDidUpdateforB只会被调用一次Brender完成。renderB后会完成Arender调用成功,因为孩子们第一次呈现,由于父母是一部分。这意味着您的问题的答案是:componentDidUpdate在所有孩子的renders 完成后执行

不确定某处是否有更深入的文档,但您自己测试确实很容易。

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阶段,并且是该阶段中调用的最后一个生命周期方法(在getSnapshotBeforeUpdatecomponentWillUnmount和 之后componentDidMount),因此将始终在渲染节点及其子节点之后发生。

render阶段以“深度优先”的方式遍历 Fiber 树,如下图所示(摘自强烈推荐的Inside Fiber:React 中新协调算法的深入概述)。

(子节点水平显示,因此c1是 的子节点b2b3是 的兄弟节点b2,而b1没有任何子节点。)

Inside Fiber:深入了解 React 中的新协调算法

此外,组件可能会重新渲染(例如,在状态更改、HOC、连接的props等之后)并且这不会触发componentDidUpdate父级上的a 我认为情况总是如此,甚至在 v16.0 之前也是如此。

综上所述,我认为对子组件的渲染做出假设是不寻常的(并且可能是不安全的),并且会在这些组件之间造成不必要的耦合,并建议每个组件都足够module化,不依赖于其父组件componentDidUpdate或子组件render(). 如果您需要通知父子(ren)已完成渲染,您可以将函数作为 prop 传递,然后在componentDidUpdateor componentDidMount(或useEffect)中调用该函数

沙盒:https : //codesandbox.io/s/react-rendering-tr59s