v19.2Latest

Масштабирование с помощью Reducer и Context

Редьюсеры позволяют объединить логику обновления состояния компонента. Контекст позволяет передавать информацию глубоко вниз другим компонентам. Вы можете объединить редьюсеры и контекст вместе для управления состоянием сложного экрана.

Вы узнаете
  • Как объединить редьюсер с контекстом
  • Как избежать передачи состояния и dispatch через пропсы
  • Как хранить логику контекста и состояния в отдельном файле

Объединение редьюсера с контекстом

В этом примере извведения в редьюсеры, состоянием управляет редьюсер. Функция редьюсера содержит всю логику обновления состояния и объявлена внизу этого файла:

Редьюсер помогает сохранять обработчики событий короткими и лаконичными. Однако по мере роста вашего приложения вы можете столкнуться с другой трудностью.В настоящее время состояниеtasksи функцияdispatchдоступны только в компоненте верхнего уровняTaskApp.Чтобы позволить другим компонентам читать список задач или изменять его, вам нужно явнопроброситьтекущее состояние и обработчики событий, которые его изменяют, через пропсы.

Например,TaskAppпередает список задач и обработчики событий вTaskList:

И TaskListпередает обработчики событий вTask:

В небольшом примере, подобном этому, это работает хорошо, но если у вас есть десятки или сотни компонентов посередине, пробрасывание всего состояния и функций может быть довольно утомительным!

Вот почему, как альтернативу их передаче через пропсы, вы можете поместить как состояниеtasks, так и функциюdispatch в контекст.Таким образом, любой компонент нижеTaskAppв дереве может читать задачи и отправлять действия без утомительного «пробрасывания пропсов».

Вот как можно объединить редьюсер с контекстом:

  1. Создайтеконтекст.
  2. Поместитесостояние и dispatch в контекст.
  3. Используйтеконтекст в любом месте дерева.

Шаг 1: Создание контекста

ХукuseReducerвозвращает текущий списокtasksи функциюdispatch, которая позволяет их обновлять:

Чтобы передать их вниз по дереву, вам нужносоздатьдва отдельных контекста:

  • TasksContextпредоставляет текущий список задач.
  • TasksDispatchContextпредоставляет функцию, которая позволяет компонентам отправлять действия.

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

Здесь вы передаётеnullв качестве значения по умолчанию для обоих контекстов. Фактические значения будут предоставлены компонентомTaskApp.

Шаг 2: Поместите состояние и dispatch в контекст

Теперь вы можете импортировать оба контекста в вашем компонентеTaskApp. Возьмитеtasks и dispatch, возвращаемыеuseReducer(), ипредоставьте ихвсему дереву ниже:

На данный момент вы передаёте информацию как через пропсы, так и через контекст:

На следующем шаге вы уберёте передачу пропсов.

Шаг 3: Используйте контекст в любом месте дерева

Теперь вам не нужно передавать список задач или обработчики событий вниз по дереву:

Вместо этого любой компонент, которому нужен список задач, может прочитать его изTasksContext:

Чтобы обновить список задач, любой компонент может прочитать функциюdispatchиз контекста и вызвать её:

КомпонентTaskAppне передаёт никакие обработчики событий вниз, и компонентTaskListтакже не передаёт никакие обработчики событий компонентуTask.Каждый компонент читает нужный ему контекст:

Состояние по-прежнему «живёт» в компоненте верхнего уровняTaskAppи управляется с помощьюuseReducer.Но егоtasks и dispatchтеперь доступны каждому компоненту ниже в дереве через импорт и использование этих контекстов.

Перенос всей связующей логики в один файл

Вы не обязаны этого делать, но можно ещё больше упростить компоненты, переместив и редюсер, и контекст в один файл. В данный момент файлTasksContext.jsсодержит только два объявления контекстов:

Этот файл скоро станет перегруженным! Вы переместите редюсер в тот же файл. Затем объявите новый компонентTasksProviderв том же файле. Этот компонент свяжет все части вместе:

  1. Он будет управлять состоянием с помощью редюсера.
  2. Он будет предоставлять оба контекста компонентам ниже.
  3. Он будетпринимать children как проп, чтобы вы могли передавать ему JSX.

Это удаляет всю сложность и связующую логику из вашего компонентаTaskApp:

Вы также можете экспортировать функции, которыеиспользуютконтекст из файлаTasksContext.js:

Когда компоненту нужно прочитать контекст, он может сделать это через эти функции:

Это никак не меняет поведение, но позволяет в дальнейшем разделить эти контексты или добавить логику в эти функции.Теперь вся настройка контекста и редюсера находится в файлеTasksContext.js. Это позволяет компонентам оставаться чистыми и незагромождёнными, сосредоточенными на том, что они отображают, а не на том, откуда они получают данные:

Можно представить, чтоTasksProvider— это часть экрана, которая знает, как работать с задачами,useTasks— способ их чтения, аuseTasksDispatch— способ их обновления из любого компонента ниже в дереве.

Примечание

Функции, такие какuseTasks и useTasksDispatch, называютсяПользовательскими хуками.Ваша функция считается пользовательским хуком, если её имя начинается сuse. Это позволяет использовать внутри неё другие хуки, например,useContext.

По мере роста вашего приложения у вас может появиться много таких пар контекст-редьюсер. Это мощный способ масштабировать приложение иподнимать состояниебез лишних усилий, когда вам нужно получить доступ к данным глубоко в дереве.

Итоги

  • Вы можете объединить редьюсер с контекстом, чтобы любой компонент мог читать и обновлять состояние выше него.
  • Чтобы предоставить состояние и функцию dispatch компонентам ниже:
    1. Создайте два контекста (для состояния и для функций dispatch).
    2. Предоставьте оба контекста из компонента, который использует редьюсер.
    3. Используйте любой из контекстов в компонентах, которым нужно их читать.
  • Вы можете дополнительно упростить компоненты, переместив всю связующую логику в один файл.
    • Вы можете экспортировать компонент, напримерTasksProvider, который предоставляет контекст.
    • Вы также можете экспортировать пользовательские хуки, такие какuseTasks и useTasksDispatch, для чтения этого контекста.
  • В вашем приложении может быть много таких пар контекст-редьюсер.