В КУРСЕ?

Разбираемся в теме

Состояние в React: почему три обновления не всегда увеличивают счётчик на три?

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

Запрос обновления не меняет текущий снимок

Компонент получает состояние для конкретного рендера и на его основе описывает интерфейс. Вызов функции обновления просит React подготовить новое состояние. Он не переписывает задним числом переменную в уже выполняющемся обработчике. Представим счётчик, текущее значение которого равно нулю. Если обработчик три раза вычисляет следующее значение как текущее плюс один, каждое вычисление опирается на тот же ноль. В очередь попадают три просьбы установить единицу, а не три последовательных увеличения. Это объяснение касается значения, захваченного конкретным рендером. Оно помогает понять и отложенный обработчик: позднее выполнение само по себе не гарантирует, что функция увидит состояние из нового рендера.

Функция обновления описывает переход

Когда новый результат зависит от предыдущего состояния, можно передать функцию обновления. Она получает предыдущее значение из очереди и возвращает следующее. Последовательность таких функций позволяет выразить несколько изменений, каждое из которых учитывает результат предыдущего. Для учебного счётчика три перехода увеличить на один преобразуют ноль сначала в единицу, затем в двойку и тройку. Отличие состоит не в количестве вызовов, а в том, что передано: готовое значение или правило вычисления по предыдущему. Функция обновления должна вычислять состояние без побочных действий. Отправку сообщения, запись во внешнюю систему или другую операцию не стоит прятать в такое вычисление. Логику изменения данных и внешний эффект необходимо организовать так, чтобы повторная проверка вычисления не вызывала нежелательных действий.

Объект в состоянии рассматривают как неизменяемый

Если состояние содержит объект, изменение его поля напрямую не является корректной заменой обновления состояния. Полезно создавать новый объект с нужным изменением, сохраняя прежний снимок. Для вложенных данных учитывают и уровни, на которых происходит изменение. Та же идея применяется к массивам. Добавление или удаление элемента оформляют через новый массив, а не через незаметное изменение прежнего. Это помогает компоненту и связанным частям интерфейса работать с понятными версиями данных. При поиске ошибки полезно отдельно ответить на три вопроса: из какого рендера пришло значение, какое обновление поставлено в очередь и не изменялся ли существующий объект напрямую. Такой разбор точнее попытки добавить случайную задержку. Таймер может изменить порядок событий, но не исправляет непонимание источника значения. Небольшой пример со счётчиком позволяет проверить модель до переноса её в сложную форму или список.

Попробуйте на практике

Проследите очередь обновлений счётчика на бумаге, не устанавливая дополнительные инструменты.

  1. Запишите исходное значение ноль и три последовательные команды установить текущее значение плюс один, где текущее относится к одному исходному снимку.
  2. Вычислите значение каждой команды отдельно. Отметьте, почему все три передают одинаковый результат.
  3. Во втором варианте запишите три функции увеличить предыдущее значение на один. Передавайте результат каждой следующей функции и сравните итог.
  4. Добавьте пример объекта с полем счётчика. Опишите словами, как получить новый объект с изменённым полем, сохранив исходный.

Как проверить результат. В первом варианте итоговая установка даёт единицу, во втором последовательные переходы дают тройку. Вы различаете снимок, готовое значение и функцию обновления.

Частые вопросы

Нужно ли всегда использовать функцию обновления?

Она особенно полезна, когда следующее состояние вычисляется из предыдущего. Если нужно установить независимое готовое значение, можно передать его непосредственно.

Можно ли исправить такую ошибку задержкой?

Случайная задержка не меняет значение, захваченное старым обработчиком. Сначала нужно определить правильный способ обновления и источник данных.

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

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

Зарегистрироваться
← К списку материалов