Состояние компонента

Что делает setState?

Метод setState() планирует изменение объекта состояния (state) компонента. Когда состояние меняется, компонент рендерится повторно.

Какая разница между state и props?

props (намеренно сокращённо от англ. «properties» — свойства) и state — это обычные JavaScript-объекты. Несмотря на то, что оба содержат информацию, которая влияет на то, что увидим после рендера, есть существенное различие: props передаётся в компонент (служат как параметры функции), в то время как state находится внутри компонента (по аналогии с переменными, которые объявлены внутри функции).

Несколько полезных ресурсов для дальнейшего изучения, в каких случаях использовать props, а в каких — state:

Почему setState даёт неверное значение?

В React как this.props, так и this.state представляют значения, которые уже были отрендерены, например, то, что видите на экране.

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

Пример кода, который не будет работать так, как ожидаем:

incrementCount() {
  // Примечание: это *не* сработает, как ожидалось.
  this.setState({count: this.state.count + 1});
}

handleSomething() {
  // Допустим, что `this.state.count` начинается с 0.
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();
  // Когда React делает последующий рендер компонента, `this.state.count` будет 1, хотя мы ожидаем 3.

  // Так происходит, потому что функция `incrementCount()` берёт своё значение из `this.state.count`,
  // но React не обновляет `this.state.count`, пока компонент не отрендерится снова.
  // Получается, что `incrementCount()` обращается к текущему значению `this.state.count`, а это 0 каждый раз, и добавляет 1.

  // Как исправить это — разберём ниже!
}

Далее перейдём к исправлению указанной проблемы.

Как обновить состояние значениями, которые зависят от текущего состояния?

Нужно добавить функцию вместо объекта к setState, которая будет срабатывать только на самой последней версии состояния (пример ниже).

В чём разница между добавлением объекта или функции к setState?

Добавление функции даёт вам доступ к текущему состоянию внутри самой функции. Так как setState вызовы «сгруппированы», это помогает связать изменения и гарантирует, что они будут выполняться друг за другом, а не конфликтовать.

incrementCount() {
  this.setState((state) => {
    // Важно: используем `state` вместо `this.state` при обновлении.
    return {count: state.count + 1}
  });
}

handleSomething() {
  // Возьмём снова для примера, что `this.state.count` начинается с 0.
  this.incrementCount();
  this.incrementCount();
  this.incrementCount();

  // Если посмотреть на значение `this.state.count` сейчас, это будет по-прежнему 0.
  // Но когда React отрендерит компонент снова, будет уже 3.
}

Прочитать больше про setState

Когда setState работает асинхронно?

В настоящее время setState работает асинхронно внутри обработчиков событий.

Это даёт гарантию, например, когда Родитель и Ребёнок вызывают setState во время клика, Ребёнок не будет рендериться дважды. Вместо этого React «откладывает» обновление состояния в самый конец событий в браузере. Это помогает сильно повысить производительность больших приложений.

Но не стоит полностью полагаться на такое поведение. В будущих версиях React будет использовать отложенные обновления состояния по умолчанию не только в обработчиках событий.

Почему React не обновляет this.state синхронно?

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

Вы можете задаваться вопросом: почему React не может просто сразу обновить this.state без повторного рендеринга?

На это есть две причины:

  • Это нарушит логику работы props и state, а значит станет причиной многих багов, которые будет сложно исправить.
  • Это сделало бы невозможным реализацию некоторых возможностей, над которыми мы сейчас работаем.

Этот GitHub-комментарий рассматривает конкретные примеры, которые помогут глубже изучить этот вопрос.

Стоит ли использовать такие библиотеки, как Redux или MobX?

Возможно.

Но вообще будет здорово сначала изучить React, прежде чем переходить к библиотекам. Можно создать готовое рабочее приложение, используя только React.