Паттерны и контекст
1. Паттерны
В React паттерны различаются по сложности и области применения. От простых, повышающих читабельность, чистоту и однородность кода, до архитектурных, позволяющих повторно использовать логику, повышая поддерживаемость кода. Все библиотеки в экосистеме React используют несколько архитектурных паттернов.
1.1. Higher-Order Component
HOC
— функция, которая принимает компонент как аргумент и возвращает новый компонент (функцию или класс) с расширенным функционалом.
Применяется когда нужно использовать повторяющуюся логику, применяемую к ряду компонентов, тем самым дополнив их функционал.
HOC должен быть чистой функцией без побочных эффектов.
HOC нельзя использовать в методе
render()
. Композиция должна быть статической, то есть во время экспорта компонента.
1.1.1. Создание и использование
Использование компонента высшего порядка называется статическая композиция
, потому что создание обертки происходит один раз, при экспорте оборачиваемого компонента.
1.1.2. withLog
HOC который просто логирует все пропсы полученные компонентом.
1.1.3. withFetch
Такой HOC можно использовать в компонентах где необходимо получить данные от сервера.
Теперь используем с любым компонентом, делая код самого компонента чище и убирая потребность в классе.
1.1.4. withToggle
HOC позволяющий сделать переключаемым любой компонент.
Получился HOC, который рендерит кнопку для скрытия или отображения контента. А что если мы хотим другой элемент управления? Тогда придется передать состояние isOpen
и метод toggle
пропсами в WrappedComponent
. Это загрязняет его пропсы и приводит к проблеме которая называется prop collision
. В случае когда необходимо рендерить разметку, лучше использовать паттерн Render Prop
.
1.1.5. Дополнительные материалы
1.2. Render Prop
Идея использования паттерна заключается в передаче управления рендером другому компоненту, а сам Render Prop
отвечает только за состояние и его обновление. Для этого в проп children
передается функция.
1.2.1. Toggler
Компонент позволяющий сделать переключаемым любой другой компонент. <Toggler>
управляет только изменением состояния, а то как это состояние будет использовано его не интересует.
Использование паттерна Render Prop
это динамическая композиция. <Toggler>
открывает доступ к состоянию и методу для его изменения. Разработчик может использовать одинаковую логику для разных элементов интерфейса.
1.2.2. Autocomplete и TagList
Пример повторного использования одной логики фильтрации для создания внешне различных компонентов.
1.2.3. Дополнительные материалы
1.3. Дополнительные материалы
2. Context API
Данные передаются сверху вниз через пропсы, но это может быть неудобно для определенных типов данных (глобальных), которые требуются многим компонентам на разных уровнях в приложении (локализация, тема оформления, состояние авторизации и др.).
Контекст обеспечивает способ передачи данных глубоко по дереву компонентов без необходимости явно передавать пропсы в промежуточные компоненты вручную на каждом уровне.
Не используйте контекст чтобы избежать передачи пропсов на несколько уровней вниз.
2.1. React.createContext()
Создает объект контекста содержащий пару компонентов:
<Context.Provider>
(поставщик) и<Context.Consumer>
(потребитель).При рендере, потребитель прочитает текущее значение контекста из ближайшего соответствующего поставщика выше в дереве компонентов.
Аргумент
defaultValue
используется потребителем, если у него нет соответствующего поставщика над ним в дереве.
2.1.1. Свойство displayName
Объекту полученному из функции React.createContext()
можно задать строковое свойство displayName
. React DevTools использует это свойство при отображении контекста.
К примеру, следующий компонент будет отображаться под именем ThemeContext
в DevTools:
2.2. Provider
Компонент, позволяющий потребителям подписываться на изменения контекста. Используется для создания и передачи контекста.
Принимает проп
value
- значение контекста, которое будет передано потомкам-потребителям этого контекста.Позволяет потребителям подписываться на изменения контекста независмо от глубины вложености.
Один провайдер может быть связан со многими потребителями.
Провайдеры могут быть вложены друг в друга.
2.3. Consumer
Компонент, который подписывается на изменения контекста. Получает текущий контекст из ближайшего сопоставимого <Provider>
выше в дереве.
Релизован по патерну
Render Prop
, поэтому ожидает функцию в качестве дочернего элемента.Параметр
context
, будет содержать значение контекста ближайшего провайдера для этого контекста выше в дереве.Если для этого контекста нет провайдера, аргумент
context
будет содержать значение по умолчанию, которое было передано вReact.createContext()
.Все потребители, являющиеся потомками провайдера, будут ре-рендериться всякий раз, когда изменяется значение контекста (пропа
value
).Потребитель обновляется даже тогда, когда компонент-предок выше в дереве отказался ре-рендериваться используя
shouldComponentUpdate()
.
2.4. Контекст темы
2.5. HOC для подписки на контекст
Чаще всего контекст потребляется многими компонентами, и явно оборачивать каждый компонент с помощью <Context.Consumer>
не лучший подход. Создадим компонент высшего порядка для подписки на контекст.
Теперь любой компонент, который зависит от контекста темы, может легко подписаться на него с помощью созданной нами функции withTheme
.
2.6. Производительность
Поскольку контекст использует ссылочную идентификацию, чтобы определить, когда нужно проводить ре-рендер, существуют некоторые подводные камни, которые могут вызвать лишние отрисовки в потребителях, когда ре-рендерится родитель провайдера.
Например, приведенный ниже код будет повторно отрисовывать всех потребителей каждый раз, когда обновляется родитель провайдера, потому что для value
всегда создается новый объект (новая ссылка).
Лучший вариант решения этой проблемы - создать отдельный компонент полностью инкапсулирующий всю логику создания и управления контекстом. Методы класса так же необходимо поместить в состояние и передавать ссылку на него, тогда ре-рендер потребителей будет происходить только тогда, когда изменится состояние провайдера.
Last updated
Was this helpful?