Почему вызов метода реагирования 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обратного вызова печатается так же, valueкак и первая. console.logПочему я не вижу результат this.setState({value: event.target.value})в области handleChangeобратного вызова?

tarrsalah
источник
Если вы используете хуки, взгляните на метод set useState, который не отражает изменения немедленно .
Эмиль Бержерон

Ответы:

616

Из документации React :

setState()не немедленно мутирует, this.stateно создает ожидающий переход состояния. Доступ this.stateпосле вызова этого метода может потенциально вернуть существующее значение. Нет гарантии синхронной обработки вызовов, setStateи вызовы могут быть сгруппированы для повышения производительности.

Если вы хотите, чтобы функция выполнялась после изменения состояния, передайте ее как обратный вызов.

this.setState({value: event.target.value}, function () {
    console.log(this.state.value);
});
Майкл Паркер
источник
Хороший ответ. Наблюдение, которое мне нужно сделать, это быть осторожным, чтобы использовать valueLink. Это хорошо работает, если вам не нужно форматировать / маскировать ввод.
Дерик
42
Вы также можете проверить componentDidUpdate. Он будет вызван после изменения состояния.
Keysox
1
Быстрый вопрос, если я могу, я вижу, как только мы передадим функцию, которая нам нужна в качестве обратного вызова, для setState, я надеялся, что функция будет выполнена в первую очередь перед вызовом render (). Но я вижу, что порядок setState () -> render () -> setStates 'callback (). это нормально? Что если мы хотим контролировать наш рендеринг на основе того, что мы делаем в обратном вызове? shouldComponentUpdate ?
semuzaboi
2
Изменение состояния компонента всегда будет вызывать повторную визуализацию, если только в shouldComponentUpdateэтом случае не указано иное. Что именно вы пытаетесь сделать в обратном вызове, который вы передаете, setStateкоторый вы хотите выполнить перед повторным рендерингом?
Майкл Паркер
4
...Зачем? Может ли кто-нибудь это оправдать?
JackHasaKeyboard
49

Как упомянуто в документации React, нет гарантии setStateсинхронного запуска, поэтому вы console.logможете вернуть состояние до его обновления.

Майкл Паркер упоминает прохождение обратного вызова в setState. Другой способ обработки логики после изменения состояния - это componentDidUpdateметод жизненного цикла, который рекомендуется в документах React.

Обычно мы рекомендуем использовать componentDidUpdate () для такой логики.

Это особенно полезно, когда могут быть setStateзапущены последовательно , и вы хотели бы запускать ту же функцию после каждого изменения состояния. Вместо добавления обратного вызова к каждому setState, вы можете поместить функцию внутри componentDidUpdate, с определенной логикой внутри, если это необходимо.

// example
componentDidUpdate(prevProps, prevState) {
  if (this.state.value > prevState.value) {
    this.foo();  
  }
}
Йо Вакита
источник
16

Вы можете попробовать использовать ES7 async / await. Например, используя ваш пример:

handleChange: async function(event) {
    console.log(this.state.value);
    await this.setState({value: event.target.value});
    console.log(this.state.value);
}
kurokiiru
источник
Чем ваш ответ отличается от другого качественного ответа?
Тод
9
Другой ответ касается использования обратного вызова в setState (). Я думал, что поместил это здесь для тех, кто не использует вариант использования обратного вызова. Например, когда я сам столкнулся с этой проблемой, мой вариант использования включал случай переключения на обновленное состояние сразу после его установки. Поэтому использование async / await было предпочтительнее использования обратного вызова.
Курокиру
Повлияет ли это на производительность, если я всегда использую await всегда, когда хочу обновить какое-то состояние, а затем ждать его обновления? И если я помещу несколько ожидающих setStates в цепочку один под другим, будет ли он отображаться после каждого обновления setState? или после последнего обновления setState?
user2774480
На то, что вы спрашиваете user2774480, я полагаю, что все сводится к конкретному варианту использования, чтобы определить, какую реализацию использовать. Если в цепочке используется несколько setStates, это повлияет на производительность, и да, он будет отображаться после каждого setState, но исправьте меня, если я ошибаюсь.
курокиру
-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}`);
}
Ritwik
источник
-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);
    });
Вишал Бишт
источник