Общее состояние между компонентами
Иногда требуется, чтобы состояние двух компонентов всегда изменялось синхронно. Для этого нужно удалить состояние из обоих компонентов, переместить его к их ближайшему общему родителю и передать им через пропсы. Этот подход называетсяподнятием состояния вверхи является одним из наиболее распространённых приёмов при написании кода на React.
Вы узнаете
- Как делиться состоянием между компонентами, поднимая его вверх
- Что такое управляемые и неуправляемые компоненты
Поднятие состояния вверх на примере
В этом примере родительский компонентAccordionрендерит два отдельныхPanel:
AccordionPanelPanel
Каждый компонентPanelимеет булево состояниеisActive, которое определяет, видно ли его содержимое.
Нажмите кнопку «Показать» для обеих панелей:
Обратите внимание, что нажатие кнопки одной панели не влияет на другую — они независимы.


Изначально состояниеisActiveкаждого компонентаPanelравноfalse, поэтому оба они свёрнуты.


Нажатие кнопки любого компонентаPanelбудет обновлять только состояниеisActiveэтого конкретногоPanel.
Но теперь предположим, что требуется изменить логику так, чтобы в любой момент времени была развёрнута только одна панель.При таком дизайне развёртывание второй панели должно сворачивать первую. Как это реализовать?
Чтобы скоординировать эти две панели, нужно «поднять их состояние вверх» к родительскому компоненту в три шага:
- Удалитесостояние из дочерних компонентов.
- Передайтежёстко заданные данные от общего родителя.
- Добавьтесостояние к общему родителю и передавайте его вниз вместе с обработчиками событий.
Это позволит компонентуAccordionкоординировать оба компонентаPanelи разворачивать только один из них за раз.
Шаг 1: Удалите состояние из дочерних компонентов
Вы передадите управление состояниемisActiveкомпонентаPanelего родительскому компоненту. Это означает, что родительский компонент будет передаватьisActive в Panelкак пропс. Начните судаления этой строкииз компонентаPanel:
Вместо этого добавьтеisActiveв список пропсовPanel:
Теперь родительский компонентPanel может управлятьisActive, передавая его вниз как проп.И наоборот, компонентPanelтеперьне управляетзначениемisActive— теперь это зависит от родительского компонента!
Шаг 2: Передача жёстко заданных данных от общего родителя
Чтобы поднять состояние, необходимо найти ближайший общий родительский компонент дляобоихдочерних компонентов, которые вы хотите согласовать:
Accordion(ближайший общий родитель)PanelPanel
В этом примере это компонентAccordion. Поскольку он находится выше обеих панелей и может управлять их пропсами, он станет «источником истины» о том, какая панель сейчас активна. Заставьте компонентAccordionпередавать жёстко заданное значениеisActive(например,true) обеим панелям:
Попробуйте изменить жёстко заданные значенияisActiveв компонентеAccordionи посмотрите результат на экране.
Шаг 3: Добавление состояния общему родителю
Поднятие состояния часто меняет природу того, что вы храните как состояние.
В данном случае одновременно должна быть активна только одна панель. Это означает, что общий родительский компонентAccordionдолжен отслеживать,какаяпанель активна. Вместоbooleanзначения он может использовать число в качестве индекса активнойPanelдля переменной состояния:
КогдаactiveIndexравен0, активна первая панель, а когда он равен1— вторая.
Нажатие кнопки «Show» в любойPanelдолжно изменять активный индекс вAccordion. Panelне может напрямую устанавливать состояниеactiveIndex, потому что оно определено внутриAccordion. КомпонентAccordionдолженявно разрешитькомпонентуPanelизменять своё состояние,передавая обработчик события вниз как проп:
Теперь кнопка<button>внутриPanelбудет использовать пропonShowв качестве обработчика события клика:
На этом поднятие состояния завершено! Перемещение состояния в общий родительский компонент позволило вам координировать работу двух панелей. Использование активного индекса вместо двух флагов «показано» гарантировало, что только одна панель активна в любой момент времени. А передача обработчика событий дочернему компоненту позволила ему изменять состояние родителя.


ИзначальноAccordionимеетactiveIndexравный0, поэтому перваяPanelполучаетisActive = true


Когда состояниеAccordion,activeIndex, изменяется на1, втораяPanelполучаетisActive = trueвместо первой
Единственный источник истины для каждого состояния
В React-приложении многие компоненты будут иметь собственное состояние. Некоторые состояния могут «жить» близко к листовым компонентам (компонентам внизу дерева), например, к полям ввода. Другие состояния могут «жить» ближе к вершине приложения. Например, даже клиентские библиотеки маршрутизации обычно реализуются путем хранения текущего маршрута в состоянии React и передачи его вниз через пропсы!
Для каждого уникального фрагмента состояния вы выберете компонент, который им «владеет».Этот принцип также известен как наличие«единственного источника истины».Это не означает, что все состояние находится в одном месте — но что длякаждогофрагмента состояния существуетконкретныйкомпонент, который хранит эту информацию. Вместо дублирования общего состояния между компонентамиподнимите егов их общего родителя ипередайте внизтем дочерним компонентам, которым это нужно.
Ваше приложение будет меняться по мере работы над ним. Часто бывает, что вы перемещаете состояние вниз или обратно вверх, пока еще разбираетесь, где «живет» каждый фрагмент состояния. Это часть процесса!
Чтобы почувствовать, как это выглядит на практике с несколькими компонентами, прочитайтеМышление в React.
Резюме
- Когда вам нужно скоординировать два компонента, переместите их состояние в их общего родителя.
- Затем передайте информацию вниз через пропсы от их общего родителя.
- Наконец, передайте обработчики событий вниз, чтобы дочерние компоненты могли изменять состояние родителя.
- Полезно рассматривать компоненты как «управляемые» (управляемые пропсами) или «неуправляемые» (управляемые состоянием).
Попробуйте выполнить несколько заданий
Challenge 1 of 2:Синхронизированные поля ввода #
Эти два поля ввода независимы. Заставьте их оставаться синхронизированными: редактирование одного поля должно обновлять другое поле тем же текстом, и наоборот.
