Реагировать setState/getState и асинхронно
Почему в React нет асинхронной функции getState?
Документация говорит нам, что setState является асинхронным. Хорошо, но это означает, что мы не можем безопасно использовать this.state, и нам также необходим асинхронный getState для соблюдения порядка выполнения.
Из того, что я понимаю, мы никогда не должны использовать this.state и использовать функцию getState, например:
getState(callback) {
this.setState((prevState) => {
callback(prevState) ;
});
}
...
this.getState((curState) => {
// we now can use the current state safely
}
Что-то, что мне не хватает здесь в моем мышлении? Почему в React такой функции не существует?
-- РЕДАКТИРОВАТЬ --
Как сказал мне мой друг, это было непонятно, и, поскольку я не уверен, но первый ответ, давайте проанализируем некоторый фрагмент кода:
simpleFunc() {
setState({ "counter" : 1 });
setState({ "counter" : 2 });
this.state.counter // => no garanty about the value
getState((curState) => { // ensure curState.counter is 2 });
}
Этот простой пример показывает, что мы не можем использовать this.state напрямую во всех ситуациях, поскольку setState является асинхронным.
Вот контрольный пример, где можно использовать getState: http://codepen.io/Epithor/pen/ZLavWR?editors=0010
Короткий ответ: плохая практика, даже не уверен, что getState даст нам текущую
Обойти это легко, но тот факт, что мы можем разложить некоторые функции и использовать их, не заботясь о контексте, кажется интересным, не так ли?
Итак, когда много событий происходит в определенном порядке, некоторые события изменяют состояние, другие читают состояние: как вы можете быть уверены, когда событие читает состояние с помощью this.state, чтобы прочитать хорошее состояние, поскольку все измененные являются асинхронными?
На самом деле все о времени:
T : event 1, change state
T+1ms : event 2, change state
T+2ms : event 3, read state
T+3ms : event 4, change state
Поскольку вы не можете предсказать, когда точно произойдет setState события 1 или 2, как вы можете гарантировать, что событие 3 действительно прочитает состояние, установленное в событии 2?
Краткий ответ: события ставятся в очередь в стеке JS, тогда как изменения состояния ставятся в очередь во внутренней очереди React. Очередь внутреннего реагирования полностью разобрана перед раздачей.
3 ответа
Вы можете определенно использовать this.state
прямо в общем. Вы никогда не должны изменять состояние напрямую (this.state.foo = 0
) и вместо этого используйте setState
всякий раз, когда вы хотите изменить состояние.
Обычно setState
выглядит так:
this.setState({
foo: 0
})
Тогда вы можете смело использовать this.state.foo
например, в вашем render()
функция.
Однако здесь есть оговорка, связанная с асинхронной природой setState
, у вас нет гарантии, что вы будете иметь немедленный доступ к this.state
после setState
был вызван.
myFunc(baz) {
this.setState({
foo: baz + 1
})
console.log(this.state.foo) // not guaranteed
}
Лучше сделать
myFunc(baz) {
const bazOne = baz + 1
this.setState({
foo: bazOne
})
console.log(bazOne)
}
Или используйте setState
второй параметр функции, используемый как обратный вызов, выполняемый после завершения операции setState. В этом обратном вызове у вас будет доступ к обновленному состоянию, т.е. this.state
:
this.setState({ foo }, () => { console.log(this.state.foo) });
См.: https://facebook.github.io/react/docs/react-component.html
setState является асинхронным, так как вы не можете сразу получить доступ к измененному свойству, ОДНАКО, есть случаи, когда вы хотите выполнить действие после изменения состояния, в таких случаях вы можете сделать:
...
this.state = {
x = 1
}
...
this.setState({
x = 2
}, () => {
console.log(this.state.x) // outputs 2
});
функция setState вызывается в очереди, поэтому вы можете поставить в очередь x количество setStates, все они будут выполнены на следующем такте.
На самом деле это не ошибка / проблема, а архитектурное решение: state
не предназначен для использования в качестве простого свойства / переменной / хранилища, он специально предназначен для использования в качестве интерфейса / визуального состояния и, как таковой, не требует обновления при каждом вызове. Он использует внутреннюю очередь, так что если вы меняете состояние много раз перед рендерингом, он фактически обновит его только один раз с конечным значением и ко времени render
метод вызывается, он будет содержать правильное значение.
Если вам просто нужно хранить / извлекать информацию во время выполнения или между методами, которые выполняются на одной и той же фазе (componentWillReceiveProps
а также shouldComponentUpdate
например) можно смело использовать this.anyProperty
как всегда:
componentWillReceiveProps() {
this.value = 'guaranteed';
return true;
}
shouldComponentUpdate() {
if (this.value === 'guaranteed') {
console.log('will always return true');
}
}
componentDidUpdate() {
this.value = ''; //cleanup
}
В приведенном выше примере, если вы использовали "setState", не было бы никакой гарантии, что значение всегда будет обновляться в "shouldComponentUpdate", но это не должно иметь значения, если вы используете его по назначению. Изменения состояния гарантированно были сброшены render
время, поэтому он должен содержать только информацию, используемую на этапе рендеринга, а не транзакционные / внутренние данные для ваших объектов. Вы можете продолжать использовать свойства объекта как обычно.