javascript - 更新动态创建的组件

标签 javascript reactjs redux react-redux

情况

我有一个呈现一些子组件的父组件。 子组件应显示从父组件传递的信息。

上下文

多人战舰游戏:

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


问题

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


解决方案尝试

不要动态渲染 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'/>

最佳答案

正如@Clinton 所说,componentWillMount 只会被调用一次。所以你实际上是在启动你的 this.child 变量,但只是更新状态,你还需要更新变量的值。

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

componentWillUpdate() is invoked just before rendering when new props or state are being received. Use this as an opportunity to perform preparation before an update occurs.

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

componentWillMount() is invoked just before mounting occurs. It is called before render(), therefore calling setState() synchronously in this method will not trigger an extra rendering. Generally, we recommend using the constructor() instead for initializing state.

引用:

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

添加上下文后更新

现在,我假设您将拥有一个 DS(可能是一个对象或嵌套数组),您将在其中维护每个框(子组件)的状态。您可以遍历它并为它们中的每一个提供一个唯一的 key(可能像 {row}-{col}),现在您只需要更新状态以反射(reflect)具体 child 的变化。

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

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

现在,每当您将 say 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

关于javascript - 更新动态创建的组件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49862802/

相关文章:

javascript - ReactJS:如何在获取数据后重新渲染组件

javascript - 无法使用 React Redux 存储数据?

javascript - getDerivedStateFromError 和 componentDidCatch 之间有什么区别

具有动态字段的 JavaScript 数学

javascript - 突变观察者产生无限循环

javascript - 通过使用工具栏粘贴链接进行超文本处理

javascript - 更新深层不可变状态属性时,Redux 不更新组件

javascript - 组件挂载时将 redux 状态移植到组件状态(不需要 GET 请求)

javascript - 对从 jQuery ui 自动完成选项列表中选择的选项执行函数

javascript - 为什么 JavaScript 函数中的参数不需要 var 作为类型?