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

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

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

Компонент более высокого порядка (HOC) - это широко используемый шаблон в React для повторного использования логики компонента, путем обертывания компонента вокруг целевого компонента и передачи ему данных через его свойства. Другими словами, компонент более высокого порядка - это функция, которая принимает ваш целевой компонент в качестве аргумента и возвращает целевой компонент с дополнительными данными и функциями.

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

В приведенном выше компоненте обернутого класса положение мыши определяется с помощью API события mousemove, предоставляемого окнами браузера. Мы настраиваем прослушиватель событий и обновляем состояние, которое содержит координаты положения мыши. Класс инкапсулирует функциональность, и теперь мы можем поделиться ею с другими компонентами.

Итак, используя шаблон компонента более высокого порядка, функция withMousePosition принимает любой целевой компонент в качестве аргумента и возвращает его со всеми его существующими свойствами плюс одно дополнительное свойство: координаты mousePosition.

В этом примере мы разделили mousePosition данные координат с презентационным App компонентом. Положение динамической мыши отображается в оранжевом абзаце:

Обернутый компонент AppWithMousePosition затем может быть отображен в DOM:

Попробуйте сами подход HOC в следующей CodeSandbox.

Рендеринг реквизита

Рендеринг опоры - это способ совместного использования кода между компонентами React с помощью опоры, значение которой является функцией. Свойство часто называют render, отсюда и терминология render prop.

Давайте посмотрим, как наш пример положения мыши, представленный ранее, выглядит при реализации с использованием рендеринга:

Логика с сохранением состояния для положения мыши такая же, как мы использовали ранее в компоненте более высокого порядка.

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

Обратите внимание, что при обращении к этому шаблону также используется терминология «функция как дочерний элемент».

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

Кроме того, для получения более подробной информации о плюсах и минусах компонентов высшего порядка и свойств рендеринга, пожалуйста, прочтите эту отличную статью Майкла Джексона, соавтора React Router.

Возвращаясь к нашему примеру, теперь мы можем визуализировать презентационный компонент приложения, составив в нем компонент MousePosition. Мы визуализируем динамическую позицию мыши внутри небесно-голубого элемента <p>, который передается через функцию в опоре render:

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

Попробуйте сами рендеринг реквизита в следующем CodeSandbox.

Реагировать на хуки

Теперь давайте посмотрим, как можно использовать «крючки» для достижения цели повторного использования логики с отслеживанием состояния в ваших приложениях, используя тот же пример положения мыши:

Обратите внимание, что мы создали настраиваемую ловушку под названием useMousePosition. Это функциональный компонент, а не компонент класса, но он инкапсулирует состояние!

В нашем примере положения мыши мы используем два разных перехватчика React в теле нашей настраиваемой функции перехвата:

Хук useState позволяет нам добавлять состояние React к функциональным компонентам без необходимости преобразовывать их в компоненты класса. Перехватчик функции useState принимает начальное значение состояния в качестве аргумента и возвращает двухэлементный массив, содержащий значение состояния (mousePosition), и функцию для обновления этого значения (setMousePosition). Внизу функции видно, что мы возвращаем значение состояния mousePosition из функции.

Хук useEffect позволяет выполнять побочные эффекты в функциональных компонентах. Примерами побочных эффектов являются получение данных из API, прослушивание событий браузера и ручное изменение DOM. Хук useEffect выполняет те же задачи, что и методы жизненного цикла componentDidMount, componentDidUpdate и componentWillUnmount, объединенные в компонентах класса.

useEffect принимает функцию обратного вызова (называемую «эффектом») в качестве своего первого аргумента и запускает ее после каждого рендеринга компонента. В нашем примере эффект заключается в настройке прослушивателя событий mousemove после первого рендеринга при монтировании компонента. Возвращенный обратный вызов от эффекта, если он указан, служит для «очистки» перед размонтированием компонента. В нашем примере мы удаляем прослушиватель событий при отключении.

В обратном вызове эффекта мы настраиваем mousemove прослушиватель событий с именем handleMouseMove, который сам вызывает setMousePosition с обновленными координатами мыши всякий раз, когда пользователь перемещает мышь.

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

В нашем примере мы передаем пустой массив [], что означает, что эффект не зависит от какого-либо обновления значения состояния в нашем компоненте, т.е. наш эффект запускается только при монтировании и очищается при размонтировании. , но он не запускается ни при каких mousePosition обновлениях. Слушатель событий уже обновляет mousePosition, поэтому нет необходимости повторно визуализировать компонент, когда это происходит.

Наш useMousePosition настраиваемый хук полностью воспроизводит поведение компонентов класса, которые использовались ранее в шаблонах HOC и render-props. Он полностью инкапсулирует нужное нам поведение в очень компактном, простом для понимания и многоразовом способах.

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

Здесь мы отображаем координаты динамической мыши в бледно-зеленом теге <p>.

Попробуйте на себе подход хуков в CodeSandbox.

Резюме

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

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

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