父组件的状态更新是否可确保在ReactJS中重新安装子组件?

时间:2019-09-06 11:46:02

标签: javascript reactjs

让我们假设我们有一个父组件:

class ParentComponent extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      titleState: undefined
    };
  }

  componentWillMount() {
    // import Title string from JSON in order to assign it to titleState

    this.setState({ titleState: jsonResponse["title"] });
  }

  render() {
    return (
      <div>
        <ChildComponent title={this.state.titleState} />
      </div>
    );
  }
}

class ChildComponent extends React.Component {
  render() {
    return (
      <div>
        <h>Title is: {this.props.title}</h>
      </div>
    );
  }
}

让我们假设带有标题的服务器响应会明显延迟,因此一开始我们在屏幕上看到的是:

Title is:

但是当响应带有jsonResponse [“ title”]值时:“ Bone Collector” 我们看到:

Title is: Bone Collector

现在的问题是:

是否可以确保父组件中的状态更改将导致子组件重新呈现,以便通过道具以新的父组件状态进行更新?

3 个答案:

答案 0 :(得分:3)

反应应用diffing algorithm来查看两个trees之间的差异。 组件更新时,实例保持不变,因此在渲染之间保持状态。 React更新基础组件实例的属性以匹配新元素,并在基础实例上调用componentWillReceiveProps()componentWillUpdate()

在您的情况下,由于更改了传递给prop的{​​{1}},因此将用childComponent重新呈现子组件。但是,react将new prop value应用于子组件的diffing algorithmold rendered output,并且只会更新已更改的内容。

为满足您的好奇心,请阅读:https://reactjs.org/docs/reconciliation.html

答案 1 :(得分:1)

专门讨论帖子中的代码片段。

。可以保证父组件的状态更改将导致子组件重新呈现。

现在的问题是

为什么子组件将使用新值进行更新?

因为它是以某种方式绑定的,所以无论何时父项更改中的值,它都会将new treeold tree进行比较(The Diffing Algorithm)并标识出更改的内容和仅修补了更改的部分在树上。

<h>Title is: {this.props.title}</h>

何时不更新子组件?

只要您从父母那里获得props并将其存储在孩子的state中,并使用state值作为数据源,例如:

state ={
   title: this.props.title
}


<h>Title is: {this.state.title}</h>

现在,在这种情况下,数据源为state。当子组件mounts时仅设置一次。每当父组件中的state发生更改时,您的子组件都不知道任何props的更改,结果您的子组件将不会使用新值进行更新。

要使此方案可行,将需要一种称为componentDidUpdate的生命周期方法,在该方法中,我们可以将新的props设置为state,然后随着状态的改变,您的子组件将重新渲染。

componentDidUpdate(prevProps){
   if(prevProps.title !== this.props.title){
      this.setState({title: this.props.title})
   }
}

答案 2 :(得分:1)

如果您在setState()的{​​{1}}中调用componentWillMount(),它将不会重新渲染 Parent,但会在Child组件的初始渲染,并忽略您最初在父构造函数中设置的值。

根据React文档:

  

UNSAFE_componentWillMount()在挂载发生之前被调用。它   在render()之前调用,因此同步调用setState()   此方法不会触发额外的渲染。通常,我们   建议使用constructor()代替初始化状态。

但是,如果您异步调用Child,则setState()的状态将被更新,并且通过您的代码,发送到Parent组件的道具将被更改,从而触发重新渲染Child组件。

但是,不建议从Child进行API调用,而是从UNSAFE_componentWillMount组件的componentDidMount()进行API调用。

这个简单的示例将帮助您理解这一点:

Parent
class Parent extends React.Component{
   constructor(props){
      super(props);
      this.state = {foo: "first"};
    }
   UNSAFE_componentWillMount(){
      this.setState({foo: "second"}); //ignore the initial state value
      setTimeout(() => this.setState({foo: "third"}), 5000); 
   }
   render(){
     return <Child foo={this.state.foo}></Child>
   }
}

class Child extends React.Component{
   constructor(props){
      super(props);
   }
  
  render(){
    console.log("Child rendered", this.props.foo);
    return <div>This is the Child</div>
  }
}
ReactDOM.render(<Parent/>, document.getElementById("root"));

在以上代码段中,未在控制台中看到<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script> <div id="root"></div>,因为在Child rendered first中调用了setState(),从而覆盖了{{1 }}从componentWillMount()foo

这是因为state在挂载发生之前立即被调用,因此没有重新渲染的机会,但是初始渲染将使用first中设置的second的数据