为什么调用react setState方法不会立即改变状态?


326

我正在阅读的“ 表单”部分文档,并尝试使用此代码演示onChange用法(JSBIN)。

var React= require('react');

var ControlledForm= React.createClass({
    getInitialState: function() {
        return {
            value: "initial value"
        };
    },

    handleChange: function(event) {
        console.log(this.state.value);
        this.setState({value: event.target.value});
        console.log(this.state.value);

    },

    render: function() {
        return (
            <input type="text" value={this.state.value} onChange={this.handleChange}/>
        );
    }
});

React.render(
    <ControlledForm/>,
  document.getElementById('mount')
);

当我<input/>在浏览器中更新值时,回调console.log内的第二个与第一个handleChange相同,为什么在回调范围内看不到的结果?valueconsole.logthis.setState({value: event.target.value})handleChange


如果使用的是钩子,请查看useState set方法该方法不会立即反映更改
Emile Bergeron,

Answers:


615

从React的文档中

setState()不会立即变异,this.state但会创建待处理的状态转换。this.state调用此方法后进行访问可能会返回现有值。无法保证对呼叫的同步操作,setState并且可以为提高性能而对呼叫进行批量处理。

如果要在状态更改后执行函数,请将其作为回调传递。

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});

好答案。我需要做的观察是小心使用valueLink。如果您不必格式化/屏蔽输入,则效果很好。
Dherik

42
您可能还想签出componentDidUpdate。状态更改后将调用它。
Keysox '16

1
我是否可以提出一个简短的问题,我看到一旦我们将需要的函数作为回调传递给setState,我希望函数将在调用render()之前首先执行。但是我看到顺序是setState()-> render()-> setStates的callback()。这正常吗?如果我们想基于回调中的操作来控制渲染,该怎么办?应该ComponentUpdate吗?
semuzaboi

2
除非组件中的行为shouldComponentUpdate另有规定,否则更改组件的状态将始终触发重新渲染。您要在传递给setState您的回调中试图在重新渲染之前发生什么呢?
Michael Parker

4
...为什么?有人可以证明这一点吗?
JackHasaKeyboard'9

49

如React文档中所述,不能保证setState被同步触发,因此您console.log可以在更新之前返回状态。

迈克尔·帕克(Michael Parker)提到在内传递回调setState。状态更改后处理逻辑的另一种方法是通过componentDidUpdate生命周期方法,这是React文档中推荐的方法。

通常,我们建议将componentDidUpdate()用作此类逻辑。

当可能连续发生时,此功能特别有用 setState触发s,并且您希望在每次状态更改后触发相同的功能。除了将回调setState函数添加到每个回调函数之外,您还可以将函数放在的内部componentDidUpdate,并在必要时在其中添加特定的逻辑。

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}

16

您可以尝试使用ES7异步/等待。例如,使用您的示例:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}

您的答案与其他高质量答案有何不同?
tod

9
另一个答案是在setState()中使用回调。我以为我在不适合使用回调用例的地方放了这个。例如,当我自己面对这个问题时,我的用例涉及设置更新状态后的开关状态。因此,使用async / await优于使用回调。
kurokiiru

如果我在要更新某些状态然后等待其更新时始终使用await,这会对性能产生影响吗?而且,如果我将多个await setStates放置在彼此之间的链条中,则每次setState更新后都会呈现吗?还是在最后一次setState更新之后?
user2774480 '19


在您询问user2774480的问题上,我相信一切都取决于特定的用例,以确定要使用的实现。如果在链中使用多个setState,则会影响性能,是的,它将在每个setState之后呈现,但是如果我错了,请纠正我。
kurokiiru


-1

async-await 语法非常适合以下内容...

changeStateFunction = () => {
  // Some Worker..

  this.setState((prevState) => ({
  year: funcHandleYear(),
  month: funcHandleMonth()
}));

goNextMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

goPrevMonth = async () => {
  await this.changeStateFunction();
  const history = createBrowserHistory();
  history.push(`/calendar?year=${this.state.year}&month=${this.state.month}`);
}

-1

简单地说-this.setState({data:value})本质上是异步的,这意味着它移出了调用栈,并且只有在解决后才返回到调用栈。

请阅读有关事件循环的内容,以清晰了解JS中的异步特性以及更新为什么需要时间-

https://medium.com/front-end-weekly/javascript-event-loop-explained-4cd26af121d4

因此-

    this.setState({data:value});
    console.log(this.state.data); // will give undefined or unupdated value

由于更新需要时间。要实现以上过程-

    this.setState({data:value},function () {
     console.log(this.state.data);
    });
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.