Реагировать 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 время, поэтому он должен содержать только информацию, используемую на этапе рендеринга, а не транзакционные / внутренние данные для ваших объектов. Вы можете продолжать использовать свойства объекта как обычно.

Другие вопросы по тегам