React setState不立即更新

时间:2016-07-25 00:27:20

标签: reactjs redux state setstate

我正在研究todo应用程序。这是违规代码的一个非常简化的版本。我有一个复选框:

 <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是一个映射到dispatch to redux的函数

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

我的问题是,当我调用updateItem action和console.log状态时,它总是落后一步。如果取消选中该复选框而不是true,我仍然会将true状态传递给updateItem函数。我是否需要调用另一个函数来强制状态更新?

11 个答案:

答案 0 :(得分:72)

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

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

但是在你的情况下,因为你想要用参数调用这个函数,你需要更多的创造性,并且可能创建你自己的函数来调用道具中的函数:

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

将它们组合在一起就可以了。

答案 1 :(得分:14)

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

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

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

答案 2 :(得分:9)

当您使用当前状态的属性更新状态时,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)
    });
}

答案 3 :(得分:8)

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

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

  

状态更新可能是异步的

     

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

     

因为this.props和this.state可以异步更新,所以   不应该依赖它们的值来计算下一个状态。

     

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

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

要修复它,请使用接受函数的第二种形式的setState()   而不是一个对象。该功能将接收先前的状态   作为第一个参数,以及应用更新时的道具   作为第二个论点:

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

答案 4 :(得分:2)

我使用了rossipedia和Ben Hare的建议并做了以下事情:

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

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

答案 5 :(得分:2)

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

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

做这样的事情:

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

一般规则是,如果你发现在多个地方需要一个州,将它提升到一个共同的父母(并不总是减少),以保持只有一个单一的真相来源

答案 6 :(得分:2)

试试这个

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

showInputError函数,如果使用任何表单

进行验证

答案 7 :(得分: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])

答案 8 :(得分:1)

首先设定你的价值。继续你的工作之后。

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

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

答案 9 :(得分:1)

如上所述,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"
                }
            }));
        }
   }

答案 10 :(得分:0)

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

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