更新 React 状态时应避免的 3 个错误

2025-06-08

更新 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:更新objectarray陈述错误

让我们尝试更新一个对象的状态。
以下代码接收名字和姓氏的输入,并使用两个相应的函数更新 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 的调用是批量的,请阅读此深入评论
  • 这是我的第一个博客,我很乐意接受任何建议和反馈🤗。
鏂囩珷鏉ユ簮锛�https://dev.to/anantbahuguna/3-mistakes-to-avoid-when-updating-react-state-45gp
PREV
如何在你的 git readme 中添加一些徽章(GitHub、Gitlab 等)
NEXT
Typescript 中的类型与接口