【问题标题】:Update dynamic created components更新动态创建的组件
【发布时间】:2018-09-26 12:24:58
【问题描述】:

情况

我有一个渲染一些子组件的父组件。 子组件应该显示从父组件传递过来的信息。

上下文

多人战舰游戏:

父组件包含一个 10x10 到 10x30 按钮(子组件)的字段。如果按下按钮,则带有按钮位置的信号会发送到 api。 api决定按下的按钮是否需要改变它的颜色。


问题

通过更新父组件的状态,如果子组件是动态创建的,则不会更新子组件道具。


解决方案尝试

不要动态渲染 Childs:

在我的情况下不可能

使用redux

我认为子组件是转储/展示组件,因为它只显示信息。在我的情况下,还有 100-300 个子组件。 Redux and React

使用参考

有 100-300 个子组件...Don’t Overuse Refs


问题

我该怎么办?有没有非反模式的解决方案?

当前代码

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state={
      foo: 'BAR'
    }
    this.child=undefined;
  }
  
  componentWillMount(){
    this.child=<Child foo={this.state.foo}/>
  }  
  
  componentDidMount(){
    var that=this
    setTimeout(function(){ 
      that.setState({  //it is just here for demonstration
        foo: 'FOO'
      }) 
    }, 1000);
  }
  
  render() {
    return (
      <div>
       Is: {this.child}
       Not Dynamic created: <Child foo={this.state.foo}/>
       Actual: <p>{this.state.foo}</p>
      </div>
    );
  }
}

class Child extends React.Component {
  render() {
    return (
      <p>{this.props.foo}</p>
    );
  }
}

ReactDOM.render(<Parent />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id="app"></div>

使用@RaghavGarg 和@Clinton Blackburn 方法编写的代码

class Parent extends React.Component {
  constructor(props) {
    super(props);
    this.state={
      foo: 'BAR'
    }
    this.child=undefined;
  }
  
  componentDidMount(){
    var that=this
    setTimeout(function(){ 
      that.setState({  //it is just here for demonstration
        foo: 'FOO'
      }) 
    }, 1000);
  }
  
  renderChild(){
    return <Child foo={this.state.foo} />
  }
  
  render() {
    const child=this.renderChild()
    return (
      <div>
       Is: {child}
       Not Dynamic created: <Child foo={this.state.foo}/>
       Actual: <p>{this.state.foo}</p>
      </div>
    );
  }
}

class Child extends React.Component {
  render() {
    return (
      <p>{this.props.foo}</p>
    );
  }
}

ReactDOM.render(<Parent />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<div id='app'/>

【问题讨论】:

    标签: javascript reactjs redux react-redux


    【解决方案1】:

    就像@Clinton 所说,componentWillMount 只会被调用一次。所以你本质上是在初始化你的this.child变量,但只是更新状态,你还需要更新变量的值。

    因为您只是制作动态组件并将它们保存在类实例中(如this.child)。我建议您在componentWillUpdate 中执行相同的任务。这样,每当您的状态发生变化时,它都会反映在您的因变量中。但是请确保不要在这个生命周期方法中使用setState

    componentWillUpdate() 在新道具渲染之前被调用 或状态正在接收。以此为契机 更新前的准备工作。

    另外,考虑使用构造函数来初始化状态,而不是在componentWillMount 中使用setState

    componentWillMount() 在安装发生之前被调用。它是 在render() 之前调用,因此在同步调用setState() 此方法不会触发额外的渲染。一般来说,我们 建议使用constructor() 代替初始化状态。

    参考:

    https://reactjs.org/docs/react-component.html#unsafe_componentwillupdate https://reactjs.org/docs/react-component.html#unsafe_componentwillmount

    添加上下文后更新

    所以现在,我假设您将拥有一个 DS(可能是一个对象或一个嵌套数组),您将在该状态下维护每个框(子组件)的状态。您可以遍历它并为每个人提供一个唯一的key(可能像{row}-{col}),现在您只需要更新状态以反映特定孩子的变化。

    注意:对每个孩子使用唯一键将启用 react 内部优化重新渲染,并且不会重新渲染孩子(使用唯一键),这不会改变。请参阅下面的代码以供参考。

    this.state = {
      boxes: {
        1: { 
          1:{ status: true, /* more data */ },
          2:{ status: true, /* more data */ },
        },
        2: { 
          1:{ status: true, /* more data */ },
          2:{ status: true, /* more data */ },
        },
        3: { 
          1:{ status: true, /* more data */ },
          2:{ status: true, /* more data */ },
        },
        4: { 
          1:{ status: true, /* more data */ },
          2:{ status: true, /* more data */ },
        }
      }
    };
    
    // this will create a 4x2 box, now you will render using above object
    
    // ...somewhere in your render method
    {
      Object.keys(this.state.boxes).map(row => (
        <div key={`row-${row}`} className="row">
          {
            Object.keys(this.state.boxes[row]).map(column => (
              <Child 
                key={`box-${row}x${column}`} 
                data={this.state.boxes[row][column]} 
              />
            ))
          }
        </div>
      ))
    }
    

    现在,每当您将 2x1 框的 status 更改为 false 时,react 只会使用键 box-2x1 重新渲染子元素。

    OP comment之后更新

    shouldComponentUpdate 应该用于决定是否要在状态更改时更新组件。它是 React 组件的生命周期方法。默认情况下它返回true,但您可以根据您的条件返回false,以不更新组件。这肯定有助于保持良好的性能。

    参考:React: Parent component re-renders all children, even those that haven't changed on state change

    【讨论】:

    • 好的,我看到您的解决方案是重新渲染所有组件。因此,如果需要更新,所有 100-300 都将重新渲染。我不会遇到这种方法的性能问题吗?
    • @Colin,是的,它会引入性能问题。但是你真的想在一个父组件中渲染大约 300 个子组件,然后在父状态更新时全部改变吗?请提及您的上下文或真实代码,以便我可以更好地帮助您。
    • @Colin,如果能解决您的问题,请查看更新后的答案并分享。
    • 我试过你的代码,在 Child 组件的渲染函数中添加了一个 console.log。它被调用了 100 次,因此键对性能问题没有帮助。我发现了这个问题:stackoverflow.com/questions/40819992/… 我实现了shouldComponentUpdate 函数,现在只调用了正确的 Childs 渲染函数。
    • 该死的,我总是忘记它。您对shouldComponentUpdate 生命周期方法是完全正确的,它将解决性能问题。虽然我告诉过你的 unique key 概念在你的渲染函数被调用之后才起作用,但它只是 React 完成的一个简单优化,但并不比 shouldComponentUpdate 好。
    【解决方案2】:

    componentWillMount 仅在父组件插入 DOM 时调用一次。它可能不是您想要创建安装后需要更改的子组件的地方。 在https://reactjs.org/docs/react-component.html#the-component-lifecycle 上查看组件生命周期。

    最简单的解决方案是在render() 方法中创建子组件。当父组件更新时,子组件会重新创建。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-10
      • 2019-09-27
      • 2012-05-09
      • 2021-05-12
      • 1970-01-01
      • 2022-01-12
      • 2020-04-07
      • 1970-01-01
      相关资源
      最近更新 更多