Управление состоянием
По мере роста вашего приложения становится полезнее более осознанно подходить к организации состояния и потоку данных между компонентами. Избыточное или дублирующееся состояние — частый источник ошибок. В этой главе вы узнаете, как правильно структурировать состояние, как поддерживать логику его обновления в удобном для сопровождения виде и как делиться состоянием между удалёнными компонентами.
В этой главе
- Как рассматривать изменения UI как изменения состояния
- Как правильно структурировать состояние
- Как «поднимать состояние», чтобы делиться им между компонентами
- Как контролировать, сохраняется состояние или сбрасывается
- Как объединять сложную логику состояния в функции
- Как передавать информацию без «проброса пропсов»
- Как масштабировать управление состоянием по мере роста приложения
Реакция на ввод с помощью состояния
В React вы не изменяете UI напрямую из кода. Например, вы не пишете команды вроде «отключить кнопку», «включить кнопку», «показать сообщение об успехе» и т.д. Вместо этого вы описываете UI, который хотите видеть для различных визуальных состояний вашего компонента («начальное состояние», «состояние ввода», «состояние успеха»), а затем запускаете изменения состояния в ответ на действия пользователя. Это похоже на то, как дизайнеры думают о UI.
Вот форма викторины, построенная с использованием React. Обратите внимание, как она использует переменную состоянияstatus, чтобы определить, следует ли включать или отключать кнопку отправки, и показывать ли сообщение об успехе вместо неё.
Готовы изучить эту тему?
ПрочитайтеРеакция на ввод с помощью состояния, чтобы узнать, как подходить к взаимодействиям с мышлением, основанным на состоянии.
Выбор структуры состояния
Хорошая структура состояния может сделать разницу между компонентом, который приятно изменять и отлаживать, и компонентом, который является постоянным источником ошибок. Самый важный принцип заключается в том, что состояние не должно содержать избыточной или дублирующейся информации. Если есть ненужное состояние, легко забыть его обновить и внести ошибки!
Например, в этой форме естьизбыточнаяfullNameпеременная состояния:
Вы можете удалить её и упростить код, вычисляяfullNameво время рендеринга компонента:
Это может показаться небольшим изменением, но многие ошибки в React-приложениях исправляются именно так.
Готовы изучить эту тему?
ПрочитайтеВыбор структуры состояния, чтобы узнать, как проектировать форму состояния, чтобы избежать ошибок.
Общее состояние между компонентами
Иногда требуется, чтобы состояние двух компонентов всегда изменялось вместе. Для этого удалите состояние из обоих компонентов, переместите его к их ближайшему общему родителю, а затем передайте им через пропсы. Это называется «поднятие состояния вверх» и является одной из самых распространённых операций при написании кода на React.
В этом примере одновременно должна быть активна только одна панель. Чтобы добиться этого, вместо хранения состояния активности внутри каждой отдельной панели, родительский компонент хранит состояние и определяет пропсы для своих дочерних компонентов.
Готовы изучить эту тему?
ПрочитайтеОбщее состояние между компонентами, чтобы узнать, как поднимать состояние вверх и поддерживать синхронизацию компонентов.
Сохранение и сброс состояния
При повторном рендеринге компонента React должен решить, какие части дерева сохранить (и обновить), а какие — отбросить или создать заново с нуля. В большинстве случаев автоматическое поведение React работает достаточно хорошо. По умолчанию React сохраняет части дерева, которые «соответствуют» ранее отрендеренному дереву компонентов.
Однако иногда это не то, что требуется. В этом чат-приложении ввод сообщения и последующее переключение получателя не сбрасывает поле ввода. Это может привести к случайной отправке сообщения не тому человеку:
React позволяет переопределить поведение по умолчанию ипринудительносбросить состояние компонента, передав ему другойkey, например<Chat key={email} />. Это говорит React, что если получатель отличается, его следует считатьдругимChatкомпонентом, который нужно создать заново с нуля с новыми данными (и UI, таким как поля ввода). Теперь переключение между получателями сбрасывает поле ввода — даже если вы рендерите тот же компонент.
Готовы изучить эту тему?
ПрочитайтеСохранение и сброс состояния, чтобы узнать о времени жизни состояния и о том, как им управлять.
Вынос логики состояния в редьюсер
Компоненты с множеством обновлений состояния, разбросанных по многим обработчикам событий, могут стать сложными для восприятия. В таких случаях можно объединить всю логику обновления состояния вне компонента в одной функции, называемой «редьюсер». Ваши обработчики событий становятся краткими, потому что они только указывают «действия» пользователя. Внизу файла функция-редьюсер определяет, как состояние должно обновляться в ответ на каждое действие!
Готовы изучить эту тему?
ПрочтитеВынос логики состояния в редьюсер, чтобы узнать, как объединить логику в функции редьюсера.
Передача данных глубоко с помощью контекста
Обычно вы передаете информацию от родительского компонента дочернему через пропсы. Но передача пропсов может стать неудобной, если вам нужно передать какой-то пропс через множество компонентов или если многим компонентам нужна одна и та же информация. Контекст позволяет родительскому компоненту сделать некоторую информацию доступной для любого компонента в дереве ниже него — независимо от глубины — без явной передачи через пропсы.
Здесь компонентHeadingопределяет свой уровень заголовка, «спрашивая» ближайшийSectionо его уровне. КаждыйSectionотслеживает свой собственный уровень, спрашивая родительскийSectionи добавляя к нему единицу. КаждыйSectionпредоставляет информацию всем компонентам ниже себя без передачи пропсов — он делает это через контекст.
Готовы изучить эту тему?
ПрочтитеПередача данных глубоко с помощью контекста, чтобы узнать об использовании контекста как альтернативы передаче пропсов.
Масштабирование с помощью редьюсера и контекста
Редьюсеры позволяют объединить логику обновления состояния компонента. Контекст позволяет передавать информацию глубоко вниз другим компонентам. Вы можете объединить редьюсеры и контекст вместе для управления состоянием сложного экрана.
При таком подходе родительский компонент со сложным состоянием управляет им с помощью редьюсера. Другие компоненты на любой глубине дерева могут читать его состояние через контекст. Они также могут отправлять действия для обновления этого состояния.
Готовы изучить эту тему?
ПрочтитеМасштабирование с помощью редьюсера и контекста, чтобы узнать, как управление состоянием масштабируется в растущем приложении.
Что дальше?
Перейдите к разделуРеакция на ввод с помощью состояния, чтобы начать читать эту главу страница за страницей!
Или, если вы уже знакомы с этими темами, почему бы не почитать оЛазейках?
