反应 setState 不立即更新

React setState not Updating Immediately

我正在开发一个待办事项应用程序。这是有问题的代码的一个非常简化的版本。我有一个复选框:

 <p><input type="checkbox"  name="area" checked={this.state.Pencil}   onChange={this.checkPencil}/> Writing Item </p>

调用复选框的函数如下:

checkPencil(){
   this.setState({
      pencil:!this.state.pencil,
  }); 
  this.props.updateItem(this.state);
}

updateItem 是映射到 redux 的函数

function mapDispatchToProps(dispatch){
  return bindActionCreators({ updateItem}, dispatch);
}

我的问题是,当我调用 updateItem 操作和 console.log 状态时,它总是落后 1 步。如果复选框未选中且不为真,我仍然会得到传递给 updateItem 函数的真状态。我是否需要调用另一个函数来强制状态更新?

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

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

但是在你的情况下,因为你想要用参数调用该函数,你将不得不更有创意,也许创建你自己的函数来调用 props 中的函数:

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

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

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

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

checkPencil: () => {
   this.setState(previousState => ({
      pencil: !previousState.pencil,
   }), () => {
      this.props.updateItem(this.state);
   });
}

我参考了 rossipedia 和 Ben Hare 的建议并做了以下工作:

checkPencil(){
   this.setState({
      pencil:!this.state.pencil,
   }, this.updatingItem); 
}

updatingItem(){
    this.props.updateItem(this.state)
}

Ben 对如何解决眼前的问题有很好的回答,但我也建议避免重复状态

如果 state 在 redux 中,你的复选框应该从 prop 或 store 读取它自己的状态,而不是在它自己的组件和全局 store 中跟踪检查状态

做这样的事情:

<p>
    <input
        type="checkbox"
        name="area" checked={this.props.isChecked}
        onChange={this.props.onChange}
    />
    Writing Item
</p>

一般规则是,如果你发现多个地方都需要一个状态,将它提升到一个共同的父级(不总是 redux)以保持只有一个真实来源

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

所以 setState((state, props) => {...}) 而不是 setState(object)

原因是setState更多的是要求状态改变,而不是立即改变。 React 对那些 setState 请求进行批处理以提高性能。

意味着您正在检查的状态 属性 可能不稳定。 这是一个需要注意的潜在陷阱。

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


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

checkPencil(){
    this.setState((prevState) => {
        return {
            pencil: !prevState.pencil
        };
    }, () => {
        this.props.updateItem(this.state)
    });
}

试试这个

this.setState({inputvalue: e.target.value}, function () {
    console.log(this.state.inputvalue);
    this.showInputError(inputs[0].name);
}); 

showInputError 函数用于验证是否使用任何表单

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

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

State Updates May Be Asynchronous

React may batch multiple setState() calls into a single update for performance.

Because this.props and this.state may be updated asynchronously, you should not rely on their values for calculating the next state.

For example, this code may fail to update the counter:

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

To fix it, use a second form of setState() that accepts a function rather than an object. That function will receive the previous state as the first argument, and the props at the time the update is applied as the second argument:

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment
}));

首先设置你的值。完成你的工作后。

this.setState({inputvalue: e.target.value}, function () {
    this._handleSubmit();
}); 

_handleSubmit() {
   console.log(this.state.inputvalue);
   //Do your action
}

如上所述setState()本质上是异步的。我简单地使用 async await.

解决了这个问题

这里有一个例子供参考:

continue = async (e) => {
        e.preventDefault();
        const { values } = this.props;
        await this.setState({
            errors: {}
        });
        const emailValidationRegex = /^(([^<>()\[\]\.,;:\s@\"]+(\.[^<>()\[\]\.,;:\s@\"]+)*)|(\".+\"))@(([^<>()[\]\.,;:\s@\"]+\.)+[^<>()[\]\.,;:\s@\"]{2,})$/i;
        if(!emailValidationRegex.test(values.email)){
            await this.setState((state) => ({
                errors: {
                    ...state.errors,
                    email: "enter a valid email"
                }
            }));
        }
   }

你也可以像下面这样更新状态两次并立即更新状态,这对我有用:

this.setState(
        ({ app_id }) => ({
          app_id: 2
        }), () => {
          this.setState(({ app_id }) => ({
            app_id: 2
          }))
        } )

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

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 everytime when myArr has changed
// perform some action which will get fired everytime when myArr gets updated
   console.log('Updated State', myArr)
}, [myArr])

这是基于 React Hooks 的解决方案。

由于 React useState 异步更新状态,如果您需要查看这些更改,请在 useEffect 挂钩中检查它们。