Управление состоянием¶
По мере роста приложения необходимо более тщательно подходить к организации состояния и потоков данных между компонентами. Избыточное или дублирующееся состояние является распространенным источником ошибок. В этой главе вы узнаете, как правильно структурировать состояние, как поддерживать логику обновления состояния и как обмениваться состоянием между удаленными компонентами.
В этом разделе
- Как рассматривать изменения пользовательского интерфейса как изменения состояния
- Как правильно структурировать состояние
- Как "поднять состояние вверх", чтобы поделиться им между компонентами
- Как контролировать сохранение или сброс состояния
- Как объединить сложную логику состояния в функции
- Как передавать информацию без "бурения пропсов"
- Как масштабировать управление состояниями по мере роста вашего приложения
Реакция на ввод с состоянием¶
В React вы не будете изменять пользовательский интерфейс из кода напрямую. Например, вы не будете писать команды типа "отключить кнопку", "включить кнопку", "показать сообщение об успехе" и т. д. Вместо этого вы будете описывать пользовательский интерфейс, который вы хотите видеть для различных визуальных состояний вашего компонента ("начальное состояние", "состояние набора текста", "состояние успеха"), а затем запускать изменения состояния в ответ на ввод пользователя. Это похоже на то, как дизайнеры думают о пользовательском интерфейсе.
Вот форма викторины, построенная с использованием React. Обратите внимание, как она использует переменную состояния status
для определения того, включать или отключать кнопку отправки, а также показывать ли вместо нее сообщение об успехе.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 |
|
Готовы изучить эту тему?
Прочитайте Реагирование на ввод с помощью состояния, чтобы узнать, как подходить к взаимодействию с учетом состояния.
Выбор структуры состояния¶
Правильное структурирование состояния может сделать разницу между компонентом, который приятно модифицировать и отлаживать, и компонентом, который является постоянным источником ошибок. Самый важный принцип заключается в том, что состояние не должно содержать избыточной или дублирующейся информации. Если есть ненужное состояние, легко забыть обновить его, что приведет к ошибкам!
Например, в этой форме есть избыточная переменная состояния fullName
:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
|
Вы можете удалить его и упростить код, вычисляя fullName
во время рендеринга компонента:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
|
Это может показаться незначительным изменением, но многие ошибки в приложениях React исправляются именно таким образом.
Готовы изучить эту тему?
Прочитайте Выбор структуры состояния, чтобы узнать, как спроектировать форму состояния, чтобы избежать ошибок.
Разделение состояния между компонентами¶
Иногда вы хотите, чтобы состояние двух компонентов всегда менялось вместе. Для этого удалите состояние из обоих компонентов, переместите его к их ближайшему общему родителю, а затем передайте его вниз через пропсы. Это известно как "поднимать состояние вверх", и это одна из самых распространенных вещей, которые вы будете делать при написании кода React.
В этом примере только одна панель должна быть активна одновременно. Чтобы достичь этого, вместо того чтобы хранить активное состояние внутри каждой отдельной панели, родительский компонент хранит состояние и определяет пропсы для своих дочерних элементов.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 |
|
Готовы изучить эту тему?
Прочитайте Совместное использование состояния между компонентами, чтобы узнать, как поднять состояние вверх и синхронизировать компоненты.
Сохранение и сброс состояния¶
Когда вы перерисовываете компонент, React должен решить, какие части дерева сохранить (и обновить), а какие отбросить или создать заново. В большинстве случаев автоматическое поведение React работает достаточно хорошо. По умолчанию React сохраняет те части дерева, которые "совпадают" с ранее отрисованным деревом компонентов.
Однако иногда это не то, что вам нужно. В этом приложении для чата при наборе сообщения и последующем переключении получателя ввод не сбрасывается. В результате пользователь может случайно отправить сообщение не тому человеку:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
React позволяет отменить поведение по умолчанию и принудить компонент сбросить свое состояние, передав ему другой key
, например <Chat key={email} />
. Это говорит React, что если получатель другой, то он должен рассматриваться как другой компонент Chat
, который должен быть создан заново с новыми данными (и UI, как входы). Теперь переключение между получателями сбрасывает поле ввода - даже если вы отображаете один и тот же компонент.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
|
Готовы изучить эту тему?
Прочитайте Сохранение и сброс состояния, чтобы узнать о времени жизни состояния и о том, как им управлять.
Извлечение логики состояния в редуктор¶
Компоненты с большим количеством обновлений состояния, распределенных по множеству обработчиков событий, могут стать непомерно сложными. Для таких случаев вы можете объединить всю логику обновления состояния за пределами вашего компонента в одной функции, называемой "reducer". Ваши обработчики событий становятся лаконичными, поскольку они указывают только "действия" пользователя. В нижней части файла функция reducer определяет, как состояние должно обновляться в ответ на каждое действие!
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 |
|
Готовы изучить эту тему?
Читайте Извлечение логики состояний в редуктор, чтобы узнать, как консолидировать логику в функции reducer
.
Передача данных глубоко с контекстом¶
Обычно вы передаете информацию от родительского компонента к дочернему компоненту через props. Но передача пропсов может стать неудобной, если вам нужно передать какой-то пропс через множество компонентов, или если многим компонентам нужна одна и та же информация. Context позволяет родительскому компоненту сделать некоторую информацию доступной для любого компонента в дереве под ним - независимо от того, насколько глубоко он находится - без явной передачи ее через props.
Здесь компонент Heading
определяет уровень своего заголовка, "спрашивая" ближайший Section
о его уровне. Каждый Section
отслеживает свой собственный уровень, спрашивая родительский Section
и добавляя к нему один. Каждый Section
предоставляет информацию всем компонентам ниже него без передачи пропсов - он делает это через контекст.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 |
|
1 2 3 |
|
Готовы изучить эту тему?
Читайте Передача данных глубоко с контекстом, чтобы узнать об использовании контекста в качестве альтернативы передаче пропсов.
Масштабирование с помощью редуктора и контекста¶
Редукторы позволяют консолидировать логику обновления состояния компонента. Контекст позволяет передавать информацию другим компонентам. Вы можете объединить редукторы и контекст для управления состоянием сложного экрана.
При таком подходе родительский компонент со сложным состоянием управляет им с помощью редуктора. Другие компоненты, находящиеся в глубине дерева, могут читать его состояние через контекст. Они также могут отправлять действия для обновления этого состояния.
1 2 3 4 5 6 7 8 9 10 11 12 13 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 |
|
Готовы изучить эту тему?
Прочитайте Расширение с помощью редуктора и контекста, чтобы узнать, как управление состояниями масштабируется в растущем приложении.
Что дальше?¶
Перейдите по ссылке Реагирование на ввод с помощью состояния, чтобы начать читать эту главу страница за страницей!
Или, если вы уже знакомы с этими темами, почему бы не прочитать о Внешний доступ?
Источник — https://react.dev/learn/managing-state