【问题标题】:Does state update of parent component assures remount of child component in ReactJS?父组件的状态更新是否确保在 ReactJS 中重新安装子组件?
【发布时间】:2020-01-09 07:05:53
【问题描述】:

假设我们有一个父组件:

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

现在的问题是:

是否保证父组件的状态改变会导致子组件被重新渲染,从而通过 props 用新的父组件的状态更新?

【问题讨论】:

  • 请查看答案并向那些回答您为什么认为他们错了的人解释。谢谢。
  • 以下答案仅适用于初始状态更改,但以后在任何情况下或事件中,如果父更新的状态比子更新不会重新渲染。

标签: javascript reactjs


【解决方案1】:

React 应用diffing algorithm 来查看trees 之间的区别。 当组件更新时,实例保持不变,因此状态在渲染之间保持不变。 React 更新底层组件实例的 props 以匹配新元素,并在底层实例上调用 componentWillReceiveProps()componentWillUpdate()

在您的情况下,由于传递给childComponentprop 已更改,因此将使用new prop value 重新渲染子组件。但是,react 将在子组件的 old rendered outputnew rendered output 上应用 diffing algorithm,并且只会更新已更改的内容。

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

【讨论】:

    【解决方案2】:

    具体谈谈你在帖子中的代码片段。

    是的。保证Parent Component的状态变化会导致Child Component重新渲染。

    现在的问题是,

    为什么子组件会更新为新值?

    因为,它的绑定方式是,每当父项中的值发生更改时,它将比较 (The Diffing Algorithm) new treeold tree 并识别已更改的内容,并且补丁仅在树中更改了部分。

    <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})
       }
    }
    

    【讨论】:

      【解决方案3】:

      如果您在componentWillMount()componentWillMount() 中调用setState() Parent,那么它不会重新渲染 Child,而是在初始渲染期间使用该值Child 组件并忽略您最初在父构造函数中设置的值。

      根据 React 文档:

      UNSAFE_componentWillMount() 在挂载之前被调用。它 在 render() 之前调用,因此同步调用 setState() 在这种方法中不会触发额外的渲染。一般来说,我们 建议使用 constructor() 来初始化状态。

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

      但是从UNSAFE_componentWillMount 调用API 是非常不可取的,而是从Parent 组件的componentDidMount() 调用API。

      这个简单的例子将帮助你理解这一点:

      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>

      在上面的 sn-p 中,Child rendered first 在控制台中是看不到的,因为在 componentWillMount() 中调用了 setState(),它覆盖了 statefoo 属性从 first 到 @ 987654341@。

      这是因为componentWillMount()在挂载之前立即被调用,所以没有重新渲染的机会,但初始渲染将使用componentWillMount()中设置的state的数据。

      【讨论】:

        猜你喜欢
        • 2021-06-08
        • 1970-01-01
        • 2018-04-20
        • 2021-04-06
        • 1970-01-01
        • 1970-01-01
        • 2015-08-16
        • 2017-11-05
        • 1970-01-01
        相关资源
        最近更新 更多