Если вы какое-то время использовали React, возможно, вы почувствовали необходимость иметь копии одной и той же логики в нескольких компонентах. Вот несколько вариантов использования:

  • Бесконечная прокрутка в трех разных представлениях с разными данными
  • Компоненты, использующие данные из сторонней подписки
  • Компоненты приложения, для которых необходимо войти в пользовательские данные
  • Отображение нескольких списков (например, пользователей, местоположений) с функцией поиска
  • Улучшение различных представлений карт с одинаковой границей и тенью

Возможно, вы задавались вопросом, есть ли в React способ разделить логику между несколькими компонентами без необходимости ее переписывать.

Что ж, ты прав! Существует продвинутая техника для решения таких сквозных задач, которая называется Компоненты высшего порядка (также известные как HOC).

В этой статье мы подробно разберем HOC, когда использовать шаблон, и рассмотрим различные варианты использования с примерами из реальной жизни.

Давайте начнем с понимания того, как этот шаблон развился из функционального программирования.

Введение в функциональное программирование

Одной из тенденций, которая начала набирать обороты в последние пару лет, является функциональное программирование (FP).

Традиционный объектно-ориентированный подход поощряет декомпозицию программы на «объекты», относящиеся к определенной области. Напротив, функциональный подход побуждает разработчика разбивать программу на небольшие функции, которые затем объединяются в приложение.

React и, по сути, современный JS использует множество методов FP наряду с императивным кодом, таких как чистота, побочные эффекты, идемпотент, композиция функций, замыкания, функции высшего порядка, каррирование, ленивые вычисления и т. д.

Прежде чем двигаться дальше, я предлагаю вам взглянуть на чистые функции и побочные эффекты от FP. Это окажется полезным для понимания основ функционального программирования.

Давайте теперь углубимся в понимание функций высшего порядка и компонентов высшего порядка с помощью примеров.

Вкус функций высшего порядка в функциональном программировании

Функция высшего порядка принимает функцию в качестве аргумента и/или возвращает функцию.

Компонент высшего порядка React — это шаблон, который проистекает из природы React, которая отдает предпочтение композиции, а не наследованию.

Рассмотрим этот пример -

Что такое компоненты высшего порядка?

Компонент более высокого порядка — это функция, которая принимает компонент и возвращает новый компонент.

Компонент высшего порядка React — это шаблон, который проистекает из природы React, которая отдает предпочтение композиции, а не наследованию.

Рассмотрим этот пример -

В приведенном выше примере higherOrderComponent — это функция, которая принимает компонент с именем WrappedComponent в качестве аргумента. Мы создали новый компонент под названием HOC, который возвращает <WrappedComponent/> из функции рендеринга. Хотя это на самом деле не добавляет функциональности, оно изображает общий шаблон, которому будет следовать каждая функция HOC.

Мы можем вызвать HOC следующим образом:

const SimpleHOC = higherOrderComponent(MyComponent);

Компонент более высокого порядка преобразует компонент в другой компонент. Обратите внимание, что HOC не изменяет входной компонент. Вместо этого HOC составляет исходный компонент, оборачивая его в компонент-контейнер.

HOC – это чистая функция с нулевыми побочными эффектами.

Кодирование практичного компонента высшего порядка

Допустим, мы хотим создать список продуктов с функцией поиска. Мы хотели бы сохранить массив наших продуктов в плоском файле и загрузить его как отдельный компонент, как показано ниже:

import products from './products.json'

Создаем наш первый компонент

Начнем с создания нашего первого компонента ProductCard. Этот компонент является функциональным компонентом, который обрабатывает представление наших данных. Данные (продукт) будут получены через реквизиты, и каждый продукт будет передан компоненту ProductCard.

Рендеринг списка продуктов

Теперь нам нужен компонент, который будет перебирать данные (продукты) с помощью функции map(). Назовем этот компонент ProductsList.

Этот компонент будет отображать данные в компоненте ProductCard.

Список продуктов с функцией поиска

Мы хотим, чтобы наши пользователи могли искать элементы, используя поле ввода. Список элементов, отображаемых в приложении, должен определяться состоянием поиска. Это компонент с отслеживанием состояния, в котором пользовательский ввод хранится в значении состояния, называемом searchTerm. Назовем его ProductsListWithSearch.

searchTerm получает состояние пустой строки. Значение, введенное пользователем в поле поиска, получается и используется для установки нового состояния для searchTerm.

А что, если мы также хотим отобразить список пользователей с функцией поиска?

Мы собираемся создать новый компонент, аналогичный приведенному выше. Назовем его UsersListWithSearch.

Преобразование нашего списка продуктов с поиском в HOC

В приведенном выше коде мы сначала импортировали компонент более высокого порядка. Затем мы добавили метод filter для фильтрации данных на основе того, что пользователь вводит в поисковом запросе. Наконец, мы обернули его компонентом withSearch.

Вот и все! У нас есть полнофункциональный HOC в действии!

Ознакомьтесь с полным примером в codepen ниже -

См. Pen React Hoc Мохана Дере (@mohandere) на CodePen.

Отладка HOC

Отладка HOC может быть сложной, поскольку у нас может быть несколько экземпляров одного и того же HOC на одной странице.

WithSearch.displayName = `WithSearch(${getDisplayName(WrappedComponent)})`;

Давайте быстро сравним результаты с displayName и без него в React Developer Tools.

Без отображаемого имени

С отображаемым именем

Предостережения

Не используйте HOC внутри метода рендеринга

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

Кроме того, при изменении состояния компонента React должен вычислить, нужно ли обновлять DOM. Он делает это, создавая виртуальный DOM и сравнивая его с текущим DOM. В этом контексте виртуальный DOM будет содержать новое состояние компонента.

Если вы используете HOC внутри метода рендеринга, то идентичность HOC не может быть сохранена при рендеринге, и это влияет на общую производительность приложения.

Но проблема здесь не только в производительности — перемонтирование компонента приводит к потере состояния этого компонента и всех его дочерних компонентов.

Вместо этого мы должны применять HOC вне определения компонента, чтобы результирующий компонент создавался только один раз.

Статические методы должны быть скопированы

Использование статического метода для компонента React бесполезно, особенно если вы хотите применить к нему HOC. Когда вы применяете HOC к компоненту, он возвращает новый расширенный компонент. По сути, новый компонент не имеет статических методов исходного компонента.

Чтобы решить эту проблему, вы можете использовать hoist-non-react-statics для автоматического копирования всех статических методов, не относящихся к React:

Рефы не проходят

Вы можете захотеть передать все реквизиты обернутому компоненту. Тем не менее, вам нужно обратить внимание на рефы, так как они не проходят. Это потому, что ref на самом деле не реквизит — как и ключ, React обрабатывает его специально.

Решение этой проблемы заключается в использовании React.forwardRef API.

Вывод

На самом базовом уровне React Higher-Order Component — это функция, которая возвращает класс и является реализацией принципов функционального программирования в вашей кодовой базе. Также возможно передавать компоненты более высокого порядка в другие компоненты более высокого порядка, поскольку обычно они принимают компоненты в качестве входных данных и возвращают другие компоненты в качестве выходных данных. Однако это может привести к ненужной вложенности в дерево компонентов.

Такие библиотеки, как connect в Redux, withRouter в react-router, являются хорошими примерами того, почему мы должны думать о реализации компонентов более высокого порядка.

Мы используем компоненты более высокого порядка, прежде всего, для повторного использования логики в приложениях React. Тем не менее, они должны отображать некоторый пользовательский интерфейс. Следовательно, HOC неудобны, когда вы хотите поделиться некоторой невизуальной логикой. В таком случае хуки React кажутся идеальным механизмом повторного использования кода.

Первоначально опубликовано на https://blog.flexiple.com 3 июня 2019 г.