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

时间:2017-03-20 23:50:17

标签: reactjs

我想知道在所有子项的componentDidUpdate方法完成之后,或者在调用该组件的render方法之后,是否执行了React组件的生命周期方法render

由于协调程序以递归方式调用render方法来更新视图,因此我预感到componentDidUpdate在重新呈现组件的所有子项后执行,但是没有足够的信息documentation。什么时候调用componentDidUpdate

3 个答案:

答案 0 :(得分:13)

在组件的componentDidUpdate方法执行完毕后调用render方法。这意味着它将在所有孩子的render方法完成后调用。这在您链接的文档中暗含着:

  

将此作为在更新组件时对DOM 进行操作的机会。

组件仅在渲染后更新,因此文档暗示它在所有子节点之后调用,因此父节点已完成重新渲染(尽管a bit unclear)。只有在完成更新,子项和所有内容时,您才能对DOM进行操作。

例如,假设我们有两个组件,ABB呈现A组件。 componentDidUpdate的{​​{1}}只会在B B完成后调用。 render render的{​​{1}}将在成功调用B A后完成,因为孩子由于是父母的一部分而首先呈现。这意味着您的问题的答案是:render在所有孩子componentDidUpdate完成后执行。

答案 1 :(得分:5)

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

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。更新内部组件只会导致该组件更新。

有趣的是,它与 <form name="form1" method="post" enctype="multipart/form-data" action="api/upload"> ... <div> <label for="folderName">Folder Name</label> <input name="folderName" type="text" /> </div> <div> <label for="images">Images</label> <!-- you choose multiple files --> <input name="images" type="file" multiple /> </div> ... </form> 函数相反。外部组件首先渲染,然后渲染内部组件。

答案 2 :(得分:1)

正如@Andrew Li正确指出的那样,componentDidUpdate在所有子级都被渲染之后执行。尽管这种情况仍然存在,但是从那时起,React的光纤和解算法(从16.0版开始)可能已经改变了先前的假设。

简而言之,React将工作分为两个阶段,render(首先执行)阶段和commitrender之后执行)阶段。 componentDidUpdate属于commit阶段,是此阶段中调用的最后一个生命周期方法(在getSnapshotBeforeUpdatecomponentWillUnmountcomponentDidMount之后),因此将始终在节点及其子节点的渲染。

render阶段以这种“深度优先”的遍历方式遍历了光纤树(如强烈推荐的Inside Fiber: in-depth overview of the new reconciliation algorithm in React所示)。

(水平示出了子节点,因此c1b2的子节点,而b3b2的同级节点,而b1则不是没有孩子。)

Inside Fiber: in-depth overview of the new reconciliation algorithm in React

此外,组件可能会重新渲染(例如,在状态更改,HOC,连接的道具等之后),这不会在父对象上触发componentDidUpdate。我认为即使在v16.0之前,情况总是如此。

基于以上所述,我认为对子组件的呈现进行假设是不寻常的(并且可能是不安全的),并且会在这些组件之间造成不必要的耦合,并建议每个组件都足够模块化而不依赖于其父组件。 componentDidUpdate或其子代的render()。如果您需要通知父母孩子已经完成渲染,则可以传递一个作为道具的函数,然后在componentDidUpdatecomponentDidMount(或useEffect)内部调用该函数。

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