Почему вызов метода реагирования setState не приводит к немедленному изменению состояния?
Я читаю Forms раздел reactjs и просто попробовал этот код, чтобы продемонстрировать использование 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
обратного вызова?
Ответы
Ответ 1
Из React документация:
setState()
не сразу мутирует this.state
, а создает ожидающий переход состояния. Доступ к this.state
после вызова этого метод может потенциально вернуть существующее значение. Здесь нет гарантия синхронной работы вызовов на setState
и вызовы могут быть собраны для повышения производительности.
Если вы хотите, чтобы функция выполнялась после изменения состояния, передайте ее в качестве обратного вызова.
this.setState({value: event.target.value}, function () {
console.log(this.state.value);
});
Ответ 2
Как упоминалось в документации React, нет гарантии, что setState
будет запущен синхронно, поэтому ваш console.log
может вернуть состояние до его обновления.
Майкл Паркер упоминает прохождение обратного вызова в пределах setState
. Другим способом обработки логики после изменения состояния является метод componentDidUpdate
lifecycle, который является методом, рекомендованным в документах React.
Обычно мы рекомендуем использовать для этой логики componentDidUpdate().
Это особенно полезно, когда может быть последовательный setState
запущен, и вы хотели бы запустить ту же функцию после каждого изменения состояния. Вместо добавления обратного вызова для каждого setState
, вы можете поместить функцию внутри componentDidUpdate
, при необходимости используя определенную логику.
// example
componentDidUpdate(prevProps, prevState) {
if (this.state.value > prevState.value) {
this.foo();
}
}
Ответ 3
Вы можете попробовать использовать ES7 async/await. Например, используя ваш пример:
handleChange: async function(event) {
console.log(this.state.value);
await this.setState({value: event.target.value});
console.log(this.state.value);
}
Ответ 4
Остерегайтесь методов жизненного цикла реакции!
Я работал в течение нескольких часов, чтобы выяснить, что getDerivedStateFromProps
будет вызываться после каждого setState()
.
😂
Ответ 5
Здесь React docs говорят:
НИКОГДА не мутируйте this.state напрямую, так как вызов setState() впоследствии может заменить
мутацию, которую вы сделали. Относитесь к this.state как к неизменному.
setState() не сразу мутирует this.state, но создает состояние ожидания
переход. Доступ к this.state после вызова этого метода может потенциально
вернуть существующее значение.
Нет гарантии синхронной работы вызовов setState и вызовов
могут быть собраны для повышения производительности. setState() всегда вызывает повторную
рендеринг, если логика условного воспроизведения не реализована в
shouldComponentUpdate().
Если используются изменяемые объекты, и логика не может быть реализована в
shouldComponentUpdate(), вызывающий setState() только тогда, когда новое состояние отличается
из предыдущего состояния избежит ненужных повторных рендерингов.
Ответ 6
Ну, так глупые soloution, но, возможно, это может помочь кому-то, кто не знаком с резервами. В моем случае я решил проблему, используя временную переменную.
У меня есть тест для пользователя, и я вычисляю количество правильных ответов на вопросы, сохраняя их до состояния теста, и после ответа на последний вопрос мне сразу нужно отобразить количество правильных ответов от состояния.
Однако государство возвращало старую ценность. Поэтому я сделал это:
const numberOfCorrectQuestionsTemp = this.state.numberOfCorrectQuestions;
if (questionCorrect) {
numberOfCorrectQuestionsTemp++;
}
this.setState({
numberOfCorrectQuestions: numberOfCorrectQuestionsTemp,
});
if (isLastQuestion) {
// display the result in percentage - with actual new value
alert((100 / (this.state.numberOfQuestions)) * numberOfCorrectQuestionsTemp + '%');
}
Но лучшим решением сделать что-то после асинхронного действия является использование обратного вызова. Вы можете найти его здесь: Когда использовать обратный вызов React setState
Ответ 7
Синтаксис 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}');
}
Ответ 8
Проще говоря - 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);
});