Каталог RSS-каналов
Статистика

RSS-каналов в каталоге: 3390

Добавлено сегодня: 0

Добавлено вчера: 0

Hi-Tech / Интернет

Смогут ли React-хуки заменить компоненты высшего порядка (HOC)?

CSS-LIVE 29.08.2019 в 12:25

Жизнь во фронтенде

Смогут ли React-хуки заменить компоненты высшего порядка (HOC)?

Перевод статьи Do React Hooks Replace Higher Order Components (HOCs)? с сайта medium.com для css-live.ru, автор — Эрик Эллиотт

«Мандаринка» — снимок Малкольма Карлоу (CC-BY-2.0)

Как только API React-хуков вышел, стало появляться много вопросов о том, сможет ли он заменить другие общие библиотеки и паттерны в экосистеме React+Redux.

Хуки задумывались как замена классам и еще одна прекрасная альтернатива для композиции поведения в отдельные компоненты. Компоненты высшего порядка также полезны для композиции поведения. Очевидно, что их задачи где-то пересекаются, так не заменить ли нам компоненты высшего порядка хуками? Более чем ясно, что некоторые HOC-и они заменить могут. Но нужно ли заменять все ваши HOC-и на React-хуки?

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

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

Компонент высшего порядка — компонент, принимающий компонент и возвращающий компонент. HOC-и можно компоновать с помощью бесточечной, декларативной композиции функций. Вот пример, где логируется каждый показ страницы через API /logger:

import React, { useEffect } from 'react'; withPageLogging = Component => props => { useEffect(() => { fetch(`/logger?location=${ [removed]}`); }, []); return ; }; export default withPageLogging;

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

import compose from 'ramda'; import withRedux from './with-redux.js'; import withAuth from './with-auth.js'; import withLogging from './with-logging.js'; import withLayout from './with-layout.js'; const page = compose( withRedux, withAuth, withLogging, withLayout('default'), ); export default page;

Это создаёт иерархию компонентов, которую можно представить как:

Чтобы использовать это для страницы:

import page from '../hocs/page.js'; import MyPageComponent from './my-page-component.js'; export default page(MyPageComponent);

Это отличный паттерн, если:

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

Замечание: это не строгие правила, от которых вообще нельзя отходить. Это всего лишь подсказки и ориентиры, которые обычно хорошо помогают. Я часто делаю небольшое исключение из правила «Никаких неявных зависимостей» для HOC, который предоставляет мой Redux-провайдер. Его я называю withRedux. Как только Redux подключен, другие HOC-и могут обращаться к состоянию, чтобы авторизовывать пользователей, и так далее.

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

Зачем использовать HOC-и?

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

Отсутствие повторения (DRY) Делай что-то одно (DOT) (из философии Unix) Разделение ответственности Принцип наименьшего знания (закон Деметры)

Поскольку при неправильном применении от HOC-ов могут быть проблемы, не забывайте вот о чем, работая с ними:

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

Замечание: HOC-и — компонуемые функциональные компоненты, способные подмешивать что угодно в пропсы, переданные в обёрнутый компонент, что делает их формой функционального миксина, когда они подмешивают в себя пропсы. Предостережения насчёт функциональных миксинов актуальны и для тех HOC-ов, в которые подмешиваются пропсы.

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

Вот пример реального компонента, использующего хуки:

import React, { useState } from 'react'; import t from 'prop-types'; import TextField, { Input } from '@material/react-text-field'; const noop = () => {}; const Holder = ({ itemPrice = 175, name = '', email = '', id = '', removeHolder = noop, showRemoveButton = false, }) => { const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email); const setter = set => e => { const { target } = e; const { value } = target; set(value); }; return ( {showRemoveButton && ( { e.preventDefault(); removeHolder(id); }} > × )} ${itemPrice} ); }; Holder.propTypes = { name: t.string, email: t.string, itemPrice: t.number, id: t.string, removeHolder: t.func, showRemoveButton: t.bool, }; export default Holder;

В этом коде используется useState, чтобы отслеживать временное состояние полей формы для имени и почты:

const [nameInput, setName] = useState(name); const [emailInput, setEmail] = useState(email);

Это состояние используется только для этого компонента, поэтому хуки хорошо подходят для этой задачи.

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

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

Вот в каких случаях HOC-и не очень уместны:

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

Задачи подходят для HOC-ов, если:

Это поведение нужно не для какого-то одного компонента, а для многих (а то и всех) компонентов в приложении Это поведение не требует кучи пропсов, использующего это поведение Компоненты могут использоваться и сами по себе, без этого поведения их HOC-а. Не нужно добавлять свою логику к компоненту, который обернут HOC-ом.

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

Начните бесплатный урок на EricElliottJS.com

Эрик Эллиотт — автор книг «Композиция программного обеспечения» и «Программирование JavaScript-приложений». Как сооснователь EricElliottJS.com и DevAnywhere.io он обучает разработчиков необходимым навыкам разработки программного обеспечения. А также формирует и консультирует команды разработчиков для крипто-проектов, и он участвовал в разработке программ для Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN, BBC, и лучших артистов, включая Usher, Frank Ocean, Metallica, и многих других.

Он ведет удаленный образ жизни с самой красивой женщиной в мире.

Другие записи ленты

Смогут ли React-хуки заменить Redux? 26.08.2019 в 13:47

Пользовательские CSS-атрибуты как механизм передачи данных из JavaScript в CSS 08.08.2019 в 11:22

Фантастические веб-спецификации и где они обитают 06.08.2019 в 14:30

W3C и WHATWG: неужели долгожданный мир? 28.05.2019 в 12:04

Селектор :has() станет доступен для оформления? 17.05.2019 в 08:58

Уроки CSSbattle 22.04.2019 в 23:29

CSS и производительность сети 05.02.2019 в 10:39

Правильная шпаргалка по CSS-каскаду 26.11.2018 в 19:03

Прокачиваем навыки отладки с помощью инструментов разработчика Chrome (часть 2) 10.11.2018 в 13:45

Прокачиваем навыки отладки с помощью инструментов разработчика Chrome (часть 1) 21.10.2018 в 12:54