React:更新列表中的一项(setState)而不是全部
React: updating one item of the list (setState) instead of all
我有以下代码。当改变一个元素的值时,整个列表被重新渲染。只重绘一个元素如何避免呢?我使用 setState 和 class 组件。
import React from "react";
import "./styles.css";
class ListItem extends React.Component {
handleUpdate = () => {
this.props.onUpdate(this.props.index);
};
totalRenders = 0;
render() {
const { data: { id, value } } = this.props;
this.totalRenders += 1;
console.log("Row rendered");
return (
<div>
<strong>id: </strong>{id}:
<strong>value: </strong>{value}
<strong>renders count: </strong>{this.totalRenders}
<button className="button" onClick={this.handleUpdate}>
Change row value
</button>
</div>
);
}
}
export default class App extends React.Component {
state = { list: [{id: 'id 1', value: '11'}, {id: 'id 2', value: '22'}]};
handleUpdate = (index) => {
let newState = this.state.list.slice()
newState[index].value = Math.round(Math.random() + 10);
this.setState(() => ({
list: newState
}));
};
render() {
console.log("App rendered");
return (
<div>
{this.state.list.map((el, index) => (
<ListItem
key={el.id}
data={el}
index={index}
onUpdate={this.handleUpdate}
/>
))}
</div>
);
}
}
沙盒:https://codesandbox.io/s/autumn-architecture-ubgh51?file=/src/App.js
在您的情况下,您可以使用 shouldComponentUpdate
,参考:React document。
简单示例:
shouldComponentUpdate(nextProps, nextState) {
// Custom update conditions.
return nextProps.data.id !== this.props.data.id;
}
完整样本:
※补充说明
如果用function component
,可以用memo
来做,参考:React document.
如果您更新您的应用程序状态,那么该组件应该被更新。预期的行为,这就是它应该如何表现。现在来回答你的问题。如果连续更改不应该使您的整个应用程序 re-render。然后你应该在你的项目中管理你的状态,并且应该在其中维护任何不必要的更改 re-render.
这是一个如何实现它的例子。您可以用这个替换您的 ListItem 并自行检查。
function UpdatedListItem({ data }) {
const [row, setRow] = React.useState(data);
React.useEffect(() => setRow(data), [data]);
const { id, value } = row;
console.log("Changes in: ", data);
function handleChange() {
setRow({
...row,
value: Math.round(100 + Math.random() * 5)
});
}
return (
<div>
<strong>id: </strong>
{id}:
<strong>value: </strong>
{value}
<strong>renders count: </strong>
{this.renders}
<button className="button" onClick={handleChange}>
Change row value
</button>
</div>
);
}
解决此问题的一种方法是使用实现 shouldcomponentupdate
但是当您需要一些性能改进而不仅仅是停止重新渲染时,应该实施该方法
This method only exists as a performance optimization. Do not rely on it to “prevent” a rendering, as this can lead to bugs.
接下来您可以考虑使用 PureComponent
要以这种方式实施,您需要更改处理状态更新的方式
handleMyChange = (index) => {
// Don't mutate the state in react it could lead in to unexpected results
// let newState = [...this.state.list];
// newState[index].value = Math.round(Math.random() + 10);
const newState = this.state.list.map((item, idx) => {
if (idx === index) {
// Replacing with a new item, so the other component can understand it's a new item and re-render it.
return {
...item,
value: Math.round(100 + Math.random() * 5)
};
}
// Return the same item, so the other component do not render since it's the same item.
return item;
});
this.setState(() => ({
list: newState
}));
};
class ListItem extends React.PureComponent {
......
React.PureComponent 通过浅层 prop 和状态比较来实现它,为了将其检测为新的更新,我们必须告诉 ListItem 组件 props 不同,为此我们应该避免在数组,相反我们必须创建一个新项目并替换旧的数组元素。
这是一个很好的例子
这是更新后的 sandbox
我有以下代码。当改变一个元素的值时,整个列表被重新渲染。只重绘一个元素如何避免呢?我使用 setState 和 class 组件。
import React from "react";
import "./styles.css";
class ListItem extends React.Component {
handleUpdate = () => {
this.props.onUpdate(this.props.index);
};
totalRenders = 0;
render() {
const { data: { id, value } } = this.props;
this.totalRenders += 1;
console.log("Row rendered");
return (
<div>
<strong>id: </strong>{id}:
<strong>value: </strong>{value}
<strong>renders count: </strong>{this.totalRenders}
<button className="button" onClick={this.handleUpdate}>
Change row value
</button>
</div>
);
}
}
export default class App extends React.Component {
state = { list: [{id: 'id 1', value: '11'}, {id: 'id 2', value: '22'}]};
handleUpdate = (index) => {
let newState = this.state.list.slice()
newState[index].value = Math.round(Math.random() + 10);
this.setState(() => ({
list: newState
}));
};
render() {
console.log("App rendered");
return (
<div>
{this.state.list.map((el, index) => (
<ListItem
key={el.id}
data={el}
index={index}
onUpdate={this.handleUpdate}
/>
))}
</div>
);
}
}
沙盒:https://codesandbox.io/s/autumn-architecture-ubgh51?file=/src/App.js
在您的情况下,您可以使用 shouldComponentUpdate
,参考:React document。
简单示例:
shouldComponentUpdate(nextProps, nextState) {
// Custom update conditions.
return nextProps.data.id !== this.props.data.id;
}
完整样本:
※补充说明
如果用function component
,可以用memo
来做,参考:React document.
如果您更新您的应用程序状态,那么该组件应该被更新。预期的行为,这就是它应该如何表现。现在来回答你的问题。如果连续更改不应该使您的整个应用程序 re-render。然后你应该在你的项目中管理你的状态,并且应该在其中维护任何不必要的更改 re-render.
这是一个如何实现它的例子。您可以用这个替换您的 ListItem 并自行检查。
function UpdatedListItem({ data }) {
const [row, setRow] = React.useState(data);
React.useEffect(() => setRow(data), [data]);
const { id, value } = row;
console.log("Changes in: ", data);
function handleChange() {
setRow({
...row,
value: Math.round(100 + Math.random() * 5)
});
}
return (
<div>
<strong>id: </strong>
{id}:
<strong>value: </strong>
{value}
<strong>renders count: </strong>
{this.renders}
<button className="button" onClick={handleChange}>
Change row value
</button>
</div>
);
}
解决此问题的一种方法是使用实现 shouldcomponentupdate 但是当您需要一些性能改进而不仅仅是停止重新渲染时,应该实施该方法
This method only exists as a performance optimization. Do not rely on it to “prevent” a rendering, as this can lead to bugs.
接下来您可以考虑使用 PureComponent
要以这种方式实施,您需要更改处理状态更新的方式
handleMyChange = (index) => {
// Don't mutate the state in react it could lead in to unexpected results
// let newState = [...this.state.list];
// newState[index].value = Math.round(Math.random() + 10);
const newState = this.state.list.map((item, idx) => {
if (idx === index) {
// Replacing with a new item, so the other component can understand it's a new item and re-render it.
return {
...item,
value: Math.round(100 + Math.random() * 5)
};
}
// Return the same item, so the other component do not render since it's the same item.
return item;
});
this.setState(() => ({
list: newState
}));
};
class ListItem extends React.PureComponent {
......
React.PureComponent 通过浅层 prop 和状态比较来实现它,为了将其检测为新的更新,我们必须告诉 ListItem 组件 props 不同,为此我们应该避免在数组,相反我们必须创建一个新项目并替换旧的数组元素。
这是一个很好的例子
这是更新后的 sandbox