更新 React 状态时应避免的 3 个错误
今天,我将与大家分享在更新 React 时可能遇到的 3 种情况states
。我经常看到一些 React 新手开发者犯这些常见错误。我还会教你如何避免这些错误。
那就让我们开始吧。
情况 1:紧接着读取状态setState()
你有没有试过在 之后检查一下状态setState()
?如果没有,那就让我来帮你试试吧。
这里我们有一个计数状态,可以使用按钮来增加。
export default class App extends Component {
state = {
count: 0
}
handleClick = () => {
this.setState({
count: this.state.count+1
})
console.log(this.state.count)
}
render() {
return (
<div className="App">
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>+</button>
</div>
);
}
}
那么为什么我们没有在控制台中获得更新的状态?
原因在于调用setState
是异步的。
因此,通过调用 setState(),我们发出更新状态的请求,同时跳转到下一行。然后在更新请求完成之前,状态会被记录到控制台中。
因此,不建议
this.state
在调用 setState() 后立即访问。
如何避免——
- 如果您想在 setState 之后立即访问状态,您可以在生命周期方法中进行操作 -对于功能组件,可以使用componentDidUpdate()或useEffect 。
- 您也可以在 setState 函数中使用回调函数来实现此目的。请注意,此方法不适用于 useState hook 的 setter 函数。
感谢Geordy James提醒此方法。
案例 2:更新object
或array
陈述错误
让我们尝试更新一个对象的状态。
以下代码接收名字和姓氏的输入,并使用两个相应的函数更新 fistName 和 lastName 的状态,但发生了一些奇怪的事情。
export default class App extends Component {
state = {
name: {
firstName: "",
lastName: ""
}
};
addFirstName = e => {
this.setState({
name: {
firstName: e.target.value
}
});
};
addLastName = e => {
this.setState({
name: {
lastName: e.target.value
}
});
};
resetName = () => {
this.setState({
name: {
firstName: "",
lastName: ""
}
});
};
render() {
return (
<div className="App">
First Name:
<input value={this.state.name.firstName} onChange=
{this.addFirstName} />
<br />
<br />
Last Name:
<input value={this.state.name.lastName} onChange=
{this.addLastName} />
<br />
<br />
<button onClick={this.resetName}>Reset</button>
<h1>{`Your name is ${this.state.name.firstName} ${
this.state.name.lastName}`}</h1>
</div>
);
}
}
因此,当您输入名字时,姓氏是未定义的,反之亦然。
这是由于所谓的“浅合并”而发生的。
当您通过在 setState() 中传递一个对象来更新状态时,状态会通过浅合并进行更新。浅合并是 JavaScript 中的一个概念,如果两个对象合并,则具有相同键的属性将被第二个对象相同键的值覆盖。
因此在我们的例子中,当我们更新名字时,setState 会用传入 setState 的新对象覆盖完整的名称对象,该对象具有 firstName 或 lastName。
如何避免 -
- 使用扩展运算符(...) - 只需使用扩展运算符复制状态,然后更新状态。
addFirstName = e => {
this.setState({
name: {
...this.state.name,
firstName: e.target.value
}
});
};
addLastName = e => {
this.setState({
name: {
...this.state.name,
lastName: e.target.value
}
});
};
注意 - 此情况也适用于array
各州。
情况 3:连续多次更新状态
假设我们想要连续多次更新状态。我们可以尝试以下方式。
这里我们将 count 加 10
import React, { Component } from "react";
import "./styles.css";
export default class App extends Component {
state = {
count: 0
};
handleClick = () => {
for (let i = 0; i < 10; i++) {
this.setState({
count: this.state.count + 1
});
}
};
render() {
return (
<div className="App">
<h1>{this.state.count}</h1>
<button onClick={this.handleClick}>Add 10</button>
<button onClick={() => this.setState({ count: 0 })}>Reset</button>
</div>
);
}
}
因此,它不是增加 10,而是仅增加 1。
原因如下。
在本例中,多个更新调用被一起批处理。因此,最后一个调用会覆盖之前的调用,计数只会加 1。
如何避免——
- 在 setState() 中使用更新函数 - setState 接受的参数之一是更新函数。
handleClick = () => {
for(let i = 0;i<10;i++) {
this.setState((prevState) => {
return {
count: prevState.count + 1
}
})
}
};
这样,我们所有的更新都会被链接起来,并且更新会按照我们的需要连续发生,而不是互相覆盖。
因此,每当您的新状态值取决于当前状态值时,请使用更新程序函数,它将始终为您提供当前更新的状态。
结束语 -
useState()
由于 useState hook 的 setter 函数是唯一的,因此所有这些情况对于 hook 来说都是相同的setState()
。- 目前,setState 调用仅在事件处理程序内进行批处理,但在即将到来的中
React v17
,这将是默认行为。 - 如果您想进一步了解为什么 setState 是异步的,或者为什么对 setState 的调用是批量的,请阅读此深入评论。
- 这是我的第一个博客,我很乐意接受任何建议和反馈🤗。