从数组映射时,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
回调中的每个组件都可以根据您的逻辑拥有自己独特的道具集。但是应该重新渲染的部分会正确渲染。
我正在根据用户输入按需加载一些 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
回调中的每个组件都可以根据您的逻辑拥有自己独特的道具集。但是应该重新渲染的部分会正确渲染。