从数组映射时,React 子组件不会重新呈现

React child components don't re-render when mapped from an array

我正在根据用户输入按需加载一些 React 组件(以及其他信息)。
要渲染的组件保存在一个数组中,渲染方法使用 array.map 来包含组件。

问题是,如果我触发主应用程序组件的 forceUpdate(),映射的组件将不会更新。

代码示例:https://codesandbox.io/s/react-components-map-from-array-ekfb7

为了在 React 中更新,您必须将数据放入状态,然后 setState

setState() 安排更新组件的状态对象。当状态改变时,组件通过重新渲染来响应,这意味着用新状态更新屏幕。

import React from "react";
import "./styles.css";

class TestComponent extends React.Component {
  render() {
    return <p>{Date.now()}</p>;
  }
}

export class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      comps: [<TestComponent />],
    }
  }

  add = () => {
    this.setState({ comps: this.state.comps.concat(<TestComponent />) })
  }

  render() {
    return (
      <div className="App">
        <h1>Components map example</h1>
        <p></p>
        <h2>Static TestComponent (ok):</h2>
        <TestComponent />

        <h2>TestComponents mapped from an array (not ok):</h2>
        {
          this.state.comps.map((comp, id) => {
            return <div key={id}>{comp}</div>;
          })
        }

        <h2>All should update when the App component renders</h2>
        <p>
          <button onClick={this.add}>Add TestComponent</button>
        </p>
      </div>
    );
  }
}

日期未更新,因为您正在 add 函数中创建组件的实例,从那时起您将引用该实例而不让 React 管理更新。

这就是为什么将组件实例存储在状态或其他变量中是一种反模式。

问题演示

下面我创建了一个仍然使用 forceUpdate 的工作示例,只是为了证明问题所在。

请注意,我没有将组件置于状态,而是推入数组以增加其长度。然后 React 可以正确地管理更新。

class TestComponent extends React.Component {
  render() {
    return <p>{Date.now()}</p>;
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);

    this.comps = [1];
  }

  add() {
    this.comps.push(1);
    this.forceUpdate();
  }

  render() {
    return (
      <div className="App">
        <h1>Components map example</h1>
        <p></p>
        <h2>Static TestComponent (ok):</h2>
        <TestComponent />
        <h2>TestComponents mapped from an array (not ok):</h2>
        {this.comps.map((comp, id) => {
          return <div key={id}><TestComponent /></div>;
        })}
        <h2>All should update when the App component renders</h2>
        <p>
          <button onClick={() => this.add()}>Add TestComponent</button>
          <button onClick={() => this.forceUpdate()}>forceUpdate App</button>
        </p>
      </div>
    );
  }
}

ReactDOM.render(<App/>,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>
这仍然不是一个理想的解决方案。但它确实显示了问题所在。

更好的解决方案

如果您需要预先了解每个组件实例的更多信息,可以使数组更复杂。

我还建议使用状态来存储 comps 数组,并完全删除 forceUpdate

class TestComponent extends React.Component {
  render() {
    return <p>{Date.now()} {this.props.a} {this.props.b}</p>;
  }
}

class App extends React.Component {
  constructor(props) {
    super(props);

    this.state = {
      comps: [{ a: 'a', b: 'b' }]
    }
  }

  add = () => {
    // add your custom props here
    this.setState(prev => ({comps: [ ...prev.comps, { a: 'c', b: 'd' } ]}));
  }

  render() {
    return (
      <div className="App">
        <h1>Components map example</h1>
        <p></p>
        <h2>Static TestComponent (ok):</h2>
        <TestComponent />
        <h2>TestComponents mapped from an array (not ok):</h2>
        {this.state.comps.map((compProps, id) => {
          return <div key={id}><TestComponent {...compProps} /></div>;
        })}
        <h2>All should update when the App component renders</h2>
        <p>
          <button onClick={() => this.add()}>Add TestComponent</button>
        </p>
      </div>
    );
  }
}

ReactDOM.render(<App/>,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>

现在请注意,map 回调中的每个组件都可以根据您的逻辑拥有自己独特的道具集。但是应该重新渲染的部分会正确渲染。