乐闻世界logo
搜索文章和话题

为什么 React Setstate 没有立即更新?

9 个月前提问
5 个月前修改
浏览次数162

6个答案

1
2
3
4
5
6

React 的 setState 函数并不保证立即更新组件的状态,这是因为 React 采用了一种名为批处理更新(batched updates)的性能优化策略。当您调用 setState 时,React 实际上将这个状态更改排入一个队列中,并非立即执行状态更新。这样做的目的是为了减少不必要的DOM操作和重渲染,从而提高应用程序的性能。

这里有几个关键点解释为什么 setState 不立即更新:

  1. 异步更新setState 实际上是一个异步操作。React 会收集多次状态更改,然后一次性进行批量更新,这通常发生在浏览器的每一帧渲染结束之前。

  2. 组件生命周期:React 的设计理念是在组件的生命周期中的特定点上统一进行状态更新和渲染。如果每次调用 setState 都会立即触发重渲染,那么在处理复杂组件时会产生性能问题。

  3. 避免不必要的渲染:假设您在一个事件处理函数中连续调用了多次 setState。如果每次调用都立即更新,那么浏览器可能会进行多余的渲染操作,这显然是不高效的。通过批量更新,React 可以合并这些状态更改,并只进行一次渲染。

  4. 并发模式:在 React 的未来版本中(如 React 18 引入的并发模式),React 更加智能地调度更新,以便更好地利用浏览器的渲染能力,提供流畅的用户体验。

举个例子,假设在一个组件的事件处理函数中,你连续调用了三次 setState,每次都改变了组件状态中的一个值:

javascript
this.setState({ value: this.state.value + 1 }); this.setState({ value: this.state.value + 1 }); this.setState({ value: this.state.value + 1 });

在上述代码中,你可能期望 value 的值会增加三次。但由于 React 的批处理和异步更新,这三次调用可能会合并成一次更新,value 只增加一次。

了解 setState 是异步的,对于编写正确的 React 代码非常重要。如果你需要在状态更新后立即执行某些操作,应该使用 setState 的回调函数,或者使用生命周期方法,比如 componentDidUpdate

javascript
this.setState({ value: this.state.value + 1 }, () => { console.log('状态更新完成,新的值为:', this.state.value); });

在这个例子中,打印状态的操作会在状态更新且组件重新渲染后执行。

2024年6月29日 12:07 回复

您应该调用第二个函数作为 setState 的回调,因为 setState 是异步发生的。就像是:

shell
this.setState({pencil:!this.state.pencil}, myFunction)

然而,在您的情况下,由于您希望使用参数调用该函数,因此您必须更具创意,并且也许创建自己的函数来调用道具中的函数:

shell
myFunction = () => { this.props.updateItem(this.state) }

将它们结合在一起应该可以工作。

2024年6月29日 12:07 回复

setState()由于各种原因(主要是性能),React 中的调用是异步的。在幕后,React 会将多个调用批处理setState()为一个状态突变,然后重新渲染一次组件,而不是为每个状态更改都重新渲染。

幸运的是,解决方案相当简单 -setState接受回调参数:

shell
checkPencil: () => { this.setState(previousState => ({ pencil: !previousState.pencil, }), () => { this.props.updateItem(this.state); }); }
2024年6月29日 12:07 回复

关于 Ben Hare 的回答,如果有人想使用 React Hooks 实现相同的目标,我在下面添加了示例代码。

shell
import React, { useState, useEffect } from "react" let [myArr, setMyArr] = useState([1, 2, 3, 4]) // the state on update of which we want to call some function const someAction = () => { let arr = [...myArr] arr.push(5) // perform State update setMyArr(arr) // set new state } useEffect(() => { // this hook will get called every time myArr has changed // perform some action every time myArr is updated console.log('Updated State', myArr) }, [myArr])
2024年6月29日 12:07 回复

当您使用当前状态的属性更新状态时,React 文档建议您使用函数调用版本而setState不是对象。

所以setState((state, props) => {...})代替setState(object).

原因是这setState更多的是要求国家改变而不是立即改变。React 会批量处理这些setState提高性能的请求。

这意味着您正在检查的国有财产可能不稳定。这是一个需要注意的潜在陷阱。

有关更多信息,请参阅此处的文档: https: //facebook.github.io/react/docs/react-component.html#setstate


为了回答你的问题,我会这样做。

shell
checkPencil(){ this.setState((prevState) => { return { pencil: !prevState.pencil }; }, () => { this.props.updateItem(this.state) }); }
2024年6月29日 12:07 回复

这是因为它是异步发生的,所以意味着那时可能还没有更新......

根据 React v.16 文档,您需要使用第二种形式来setState()接受函数而不是对象:

状态更新可能是异步的

React 可以将多个 setState() 调用批处理为单个更新以提高性能。

由于 this.props 和 this.state 可能会异步更新,因此您不应依赖它们的值来计算下一个状态。

例如,以下代码可能无法更新计数器:

shell
// Wrong this.setState({ counter: this.state.counter + this.props.increment, });

要修复此问题,请使用 setState() 的第二种形式,它接受函数而不是对象。该函数将接收先前的状态作为第一个参数,并将应用更新时的 props 作为第二个参数:

shell
// Correct this.setState((prevState, props) => ({ counter: prevState.counter + props.increment }));
2024年6月29日 12:07 回复

你的答案