Реакция: this.state против this.setState

#javascript #reactjs

#javascript #reactjs

Вопрос:

Сценарий 1:

 const { foo } = this.state;
  

Сценарий 2:

 this.setState(({ foo }) => { ... });
  

foo гарантированно ли будет идентичным между этими двумя случаями? Или setState когда-нибудь будет выполняться асинхронно и возвращать другое значение на основе других setState значений?

Ответ №1:

Состояние может быть состоянием, когда вы получаете доступ к состоянию сразу после использования this.setState , как this.setState есть async .

Если вам нужно вычислить на основе обновленного состояния, т.е. (после this.state вызова), вы можете использовать 2-й аргумент для метода, который является обратным вызовом, который запускается после фиксации изменений состояния.

 this.setState(updateState, () => {
   const { foo } = this.state;

   console.log(foo); // updated foo
});

function updatedState({ foo }) { 
    // you can be sure that `foo`
    // is from the previous state 
    // before setState is called
}
  

Ответ №2:

Как упоминают документы React:

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

Таким образом, мы можем использовать второй аргумент setState для передачи обратного вызова, где мы выполняем нашу логику, которая зависит от наличия обновленного значения foo . Однако ваш первоначальный вопрос заключался в том, было ли значение foo in const { foo } = this.state; и значение foo in this.setState(({ foo }) => { ... }); одинаковым.

Чтобы проверить это, мы можем сравнить выполнение a setState , за которым следует, this.state.foo и a setState , за которым следует другой setState (второй будет просто регистрировать значение foo вместо его изменения). Пожалуйста, обратитесь к следующему фрагменту:

 class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      foo: 0,
      boo: 0
    }
  }
  
  handleClickSetState = () => {
    this.setState(({foo}) => ({foo: foo 1}));
    this.setState(({foo}) => console.log("inside setState. foo: ", foo));
  }
  
  handleClickState = () => {
    this.setState(({boo}) => ({boo: boo 1}));
    console.log("outside setState. boo: ", this.state.boo)
  }
  
  render() {
    return <React.Fragment>
      <button onClick={this.handleClickState}>Test state</button>
      <button onClick={this.handleClickSetState}>Test setState</button>
    </React.Fragment>
  }
}

ReactDOM.render(<Example />, document.getElementById("root"));  
 <script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="root"></div>  

Как мы можем видеть, «случай состояния» отстает на 1, чего и следовало ожидать, поскольку setState он является асинхронным (и мы не использовали второй аргумент setState ). Однако «setState case» показывает, что всегда отображается правильное значение, даже если мы не используем обратный вызов второго аргумента в setState.

В заключение const { foo } = this.state; всегда будет давать вам немедленное значение foo , независимо от ожидающих слияния состояний, в то время this.setState(({ foo }) => { ... }); как кажется, что сначала будут завершены ожидающие обновления перед выполнением обратного вызова, что означает, что foo всегда будет иметь самое последнее значение.

Ответ №3:

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

 this.setState(
  // function taking previous state   props,
  // which should return updated state.
  // this could also just be an object with
  // the new state, if you don't need to rely
  // on previous state.
  (previousState, props) => {
    if (previousState.something) {
      return { something: somethingElse }
    } else {
      return { something: anotherThing }
    }
  }, () => {
    // safe to use new state here,
    // since state has been updated.
    // but it's recommended to use
    // componentDidUpdate instead!
  })