Автор оригинала: Casey Morris.
вступление
Чтобы просто установить его, компонент более высокого порядка является функцией, которая принимает компонент и возвращает новый компонент. Мне нравится думать о них как о параметризованных компонентах. Много раз я оказываюсь, что создавая несколько компонентов с очень похожей на логику, причем только 1 или 2 изменения. Как только я нахожу этот корпус, как это, очень просто аннотация общая логика, и поместите логику, которая меняется в параметры.
Вы можете узнать больше о HOCS здесь , в официальных реагированных документах. Поскольку компоненты являются просто функциями, а HOCS – это просто функции, которые возвращают другие функции, мы можем использовать функциональные концепции для цепи их с использованием методов утилит, таких как составить
, который предоставляется многими библиотеками ( Он включен в redux! ).
Я собираюсь поговорить о нескольких общих случаях использования, а затем пройти их внедрение, и несколько способов их можно сочинить вместе, чтобы сделать еще более мощные HOC!
Следуйте вместе (пример приложения)
Вы можете следить с помощью простого приложения RACT IRACT, который я создал, чтобы продемонстрировать следующие концепции и использовать случаи использования. Его можно использовать в браузере или клонированном из репозитория.
Живая песочница: CodeSandox.io/s/github/seysymorrisus/composing-hocs GitHub Repo: github.com/Ssyymorrisus/composing-hocs.
Сценарии использования
Теперь, когда мы немного знаем о компонентах более высокого порядка, мы можем пройти несколько распространенных случаев использования.
Погрузчик
Обычно привлечь данные с API и отображать «спиннер» или сообщение, пока данные не будут выявлены. У вас может быть стандартный компонент загрузки, который имеет сообщение для отображения загрузочного сообщения, специфичного для вашего текущего приложения.
Ошибка
Почти идентичны HOC-загрузчику, но вместо этого отображается компонент ошибок и ожидает опоры ошибки. Может быть предоставлен пользовательский компонент ошибок.
По умолчанию
Хорошая практика для отображения некоторых типов сообщения, если данные были успешно выделены, но ни один из данного типа не был возвращен из источника данных. Например, вместо того, чтобы отобразить пустой список сообщений, если бы посты не созданы, у вас может быть компонент по умолчанию, который может предложить пользователю создавать сообщение.
Данные
Компоненты контейнера часто получают данные и передают эти данные как опоры для дочернего компонента. Как правило, единственное, что изменяется между этими компонентами контейнера, является конечной точкой API. Мы также можем использовать HOCS, чтобы легко пройти в Mock Data для целей разработки.
Mockdata
Используется для целей разработки для быстрого прототипа компонентов до того, как конечная точка API была создана в вашем реагированном проекте. Данные и задержка (для MOCK ASYNC Data Futching) поставляются в HOC. Данные передаются на обернутую компонент.
Регистратор
Легко регистрируйте все изменения в процессе (о рендере) для данного компонента, полезной для быстрой отладки.
Реквизит
Иногда вам нужно вводить реквизиты в компонент, это особенно полезно при использовании филиала HOC и требуется только одну ветвь для получения конкретных видов.
Тайм-ауты
Добавление тайм-аута, которое контролирует состояние в реакции, будет бросать ошибку, если компонент размонтирует, прежде чем функция, прилагаемая к тайм-ауту. Мы можем создать HOC, который обрабатывает и делится функциональностью, чтобы легко добавить и очистить тайм-ауты.
Контейнер
Довольно распространено, чтобы иметь контейнерную компонент, который выбирает данные и оказывает список компонентов, использующих эти данные. Мы можем объединить это с другими HOCS, такими как: asloader, haseRor, andhasdefault для дальнейшего расширения функциональности.
Список
Как указано выше, это довольно распространено, чтобы принять массив данных и представлять компонент для каждого элемента в массиве. Этот HOC также будет распространять текущий элемент данных в качестве опоры в прилагаемый компонент.
HOCS.
Теперь, когда мы прошли несколько случаев использования, давайте посмотрим на некоторые простые реализации, прежде чем мы попадем в любые сложные цепочки.
HASPROPS.
Принимает один параметр, впрыскиваемыеProops и возвращает обернутую компонент с прилагаемыми инъекционными настройками. Полезно, когда вам необходимо вводить реквизиты одному компоненту в филиале, поскольку прилагаемые опоры вводятся как в проходящие, так и неспособности.
const hasProps = injectedProps => WrappedComponent => { const HasProps = props =>return HasProps }
Использовать:
branch( // ... hasProps({sample: 'Sample Prop'})(Component) // ... )
Haslogger Принимает один параметр, префикс, который префикрует зарегистрированное сообщение, по умолчанию для пустой строки. Журналы реквизиты к консоли на каждом визуализации Зарченокомпонент
Отказ
const hasLogger = (prefix = '') => WrappedComponent => { const HasLogger = props => { console.log(`${prefix}[Props]:`, props) return} return HasLogger }
Использовать:
hasLogger(Component)
ветка
Занимает 3 параметра: Тест
, ComponentOnpass
и ComponentOnfail
и возвращает любой из прилагаемых компонентов в зависимости от результата функции тестирования. Если Тест
Функция возвращает правда
, ComponentOnpass
будет возвращен, если ложь
, ComponentOnfail
будет возвращено. Немедленно возвращает новый компонент в отличие от других HOCS в этом посте.
const branch = (test, ComponentOnPass, ComponentOnFail) => props => test ?: ComponentOnFail ? : null
Использовать:
branch(testFunction, Component1, Component2)
Hasmockdata.
Занимает 2 параметра: Mockdata
и задержка
и возвращает обернутый компонент с новым данные
пропры Mockdata
вводится в данные
опора после задержка
прошел. Можно установить задержка
к 0
Или оставьте пустым, чтобы не иметь задержки. Использует Hastimeouts
HOC расширить функциональность для включения addimeout
и Clearimeout
реквизит.
const hasMockData = (mockData, delay) => WrappedComponent => { class HasMockData extends React.Component { state = { data: [], useDefault: true } componentDidMount() { this.props.addTimeout(() => this.setState({data: mockData}), delay) } componentWillUnmount() { this.props.clearTimeouts() } render() { return} } return hasTimeouts(HasMockData) }
Использовать:
hasMockData({data: 'This is mock data'}, 1000)(Component)
asloader.
Этот компонент более высокого порядка не имеет параметров. Возвращает обернутую компонент с новой нагрузкой и ожидаемым реквизитом погрузки. Это условно оказывает прилагаемую компонент, если нагрузка ложная. В противном случае он отображает компонент загрузки с поставляемым погрузочным погрузкой.
const hasLoader = WrappedComponent => { const HasLoader = props => branch( props.loading, hasProps({message: props.loadingMessage})(Loading), WrappedComponent )(props) return HasLoader }
Использовать:
hasLoader(Component)
haserror.
Занимает 1 параметр: Errorcomponent
который имеет значение по умолчанию компонента ошибок. Может поставить пользовательский Errorcomponent
При желании, а не использовать значение по умолчанию. Возвращает завернутый компонент с новым ожидаемым опоры: Haserror
Отказ Это условно делает прилагаемый компонент, если Haserror
неверно В противном случае он делает Errorcomponent
Отказ
const hasError = (ErrorComponent = Error) => WrappedComponent => { const HasError = props => branch(props.hasError, ErrorComponent, WrappedComponent)(props) return HasError }
Использовать:
hasError(CustomErrorComponent)(Component)
Использует по умолчанию Ошибка
. Компонент, если ни один не поставлен:
hasError()(Component)
Hasdefault.
Занимает 1 параметр: По умолчанию
который ожидает быть реагированным компонентом. Возвращает завернутый компонент с новым ожидаемым опоры: Useefault
Отказ Это условно делает прилагаемый компонент, если Useefault
это ложь
Отказ В противном случае он делает По умолчанию
составная часть.
const hasDefault = Default => WrappedComponent => { const HasDefault = props => branch(props.useDefault, Default, WrappedComponent)(props) return HasDefault }
Использовать:
hasDefault(DefaultComponent)(Component)
Hasdata
Ожидает объекта одного параметра, который ожидает свойств URL
, Пармы
и LoadeMessage
Отказ URL
диктует, какую конечную точку ударил, Пармы
вводит параметры URL для запроса и LoadeMessage
Позволяет настроить загрузку в рамках Погрузчик
составная часть. Использует Axios Получить данные с конечной точки API. Axios может быть легко заменен вашим вашим внедрением выбора. Впрыскивает данные
, Haserror
, Ошибка
. , Useefault
, Загрузка
и LoadeMessage
как реквизиты будут использоваться в обернутом компонентах.
const hasData = ({url, params, loadingMessage}) => WrappedComponent => { class HasData extends React.Component { state = { data: [], hasError: false, error: { title: 'Cannot retrieve Real Posts', message: 'Could not retrieve Real Posts from supplied API.' }, useDefault: false, loading: false, loadingMessage } componentDidMount() { this.setState({loading: true}) axios.get(url, { params }) .then(({data}) => { this.setState({ data, loading: false, hasError: false, useDefault: data.length === 0 }) }) .catch(error => { console.log(error) this.setState({ hasError: true, loading: false }) }) } render() { return () } } return HasData }
Использовать:
hasData({ url: 'https://jsonplaceholder.typicode.com/posts', params: { _limit: 10, page: 2 }, loadingMessage: 'Loading posts from JSON Placeholder...' })(Component)
Hastimeouts
Не ожидает никаких параметров и возвращает обернутой компонент с инъекционными реквизитами addimeout
и Clearimeouts
Отказ Эти реквизиты позволяют легко создавать тайм-ауты и иметь возможность очистить их, когда компонент размонтирует. Если у вас есть активное время ожидания и размонтируйте компонент, он бросит ошибку.
const hasTimeouts = WrappedComponent => { class HasTimeouts extends React.Component { constructor(props) { super(props) this.timeouts = [] this.addTimeout = this.addTimeout.bind(this) this.clearTimeouts = this.clearTimeouts.bind(this) } addTimeout(func, delay) { this.timeouts.push(setTimeout(func, delay)) } clearTimeouts() { this.timeouts.forEach(clearTimeout) } render() { return () } } return HasTimeouts }
Использовать:
hasTimeouts(Component)
islist
Ожидает одного параметра Тип
который диктует классное значение, используемое в обернутом Div для списка. Этот HOC ожидает, что компонент списка имеет принцип данных, который содержит массив элементов. Все свойства отдельных предметов вводятся как реквизит для отдельного обернутого компонента.
Использовать:
isList('component-type')(Component)
Композиция с использованием компонентов высшего порядка
Истинная мощность HOCS приходит, когда вы начинаете цепь (составляют) их. Вы можете цепить как многие из них, как вам нравится. Если у вас есть компонент, который нуждается в ошибке и состоянии по умолчанию, просто составляйте это так:
compose( hasError(ErrorComponent), hasDefault(DefaultComponent) )(Component)
Возвращенный компонент теперь можно контролировать, чтобы показать ошибку или по умолчанию, используя его реквизиты. В случае из приведенного выше примера, если использование Deadefault и HaseRor являются оба правда
, Haserror
и Errorcomponent
пройдет прецедент, поскольку он сначала входит в составную функцию.
Хотите добавить вход в список вышеуказанного компонента, а также поставляйте его с помощью Mockdata? Легкий! Просто измените вышеизложенное на:
compose( hasLogger(), hasMockData(mockData, delay), hasError(ErrorComponent), hasDefault(DefaultComponent) )
HOCS может быть легко добавлен и составлен для расширения функциональности любого компонента.
iscontainer.
Ожидает единого объекта параметра с реквизитом данные
, Ошибка
. и Defaultcomponent
Отказ данные
Ожидается, что объект параметра, ожидаемый по Хашата
HOC. Ошибка
. Ожидается, что пользовательский компонент ошибок, но если оставить пустым, отобразит компонент ошибки по умолчанию. Defaultcomponent
Ожидается, что компонент отображается в списке контейнера, если из API не было извлечено никаких элементов.
const isContainer = ({data, Error, DefaultComponent}) => WrappedComponent => { const IsContainer = props =>return compose( hasData(data), hasLoader, hasError(Error), hasDefault(DefaultComponent) )(IsContainer) }
Использовать:
isContainer({ data: { url: 'https://jsonplaceholder.typicode.com/posts', params: { page: 2, _limit: 10 }, loadingMessage: "Loading Posts..." }, DefaultComponent: PostDefault })(PostList)
Быстрая отметка о отладке
Для целей отладки мы делаем наш код немного более многословным. Используя HAPPROPS HOC в качестве примера, мы могли бы еще больше упростить его так:
const hasProps = injectedProps => WrappedComponent => props =>
Проблема с этим заключается в том, что при просмотре в инструментах разработчиков реагирования компонент возвращается как Неизвестно
Как никогда не было дано имя. Используя оригинальный пример, предусмотренный для HASPROP
HOC будет отображать HASPROPS
Компонент в инструментах разработчиков реагирования.
Использование Redux Compose
Как упомянуто выше, Redux поставляет метод COMPOSE, который можно импортировать и использовать так:
import { compose } from 'redux' compose( // ...do stuff )(Component)
Вы можете прочитать больше об их реализации в их документации здесь Отказ Составная функция не очень особенная сама по себе, она может быть импортирована из многих библиотек.
Главная страница
Приведенное примерное приложение показывает использование всех концепций выше. Главная страница включает в себя компоненты, которые были расширены с HOCS, а также включает в себя кнопки для управления реквизитами, которые управляют HOCS, используемыми для их расширения. Чтобы просто поставить его, вы можете нажать кнопку и переключить, какую компонент рендерирует на основе реквизитов. Эта страница включает в себя MockPostContainer
который расширяет Пост
Компонент с регистратором, издевательствами, погрузчик, ошибка, по умолчанию и списку.
Сообщения, TODOS, комментарии и страницы пользователей
Все эти страницы потребляют реальные конечные точки API от здесь Отказ Каждый из них расширяет один компонент (пост, тоду, комментарий, пользователь) с iscontainer
и islist
HOCS.
Обертывание
Я надеюсь, что вышеуказанные модели/компоненты помогут вам создать новые компоненты более высокого порядка. Это наиболее распространенные случаи использования для меня, я уверен, что у вас, вероятно, есть несколько распространенных случаев использования, не перечисленные выше. Я хотел бы услышать, как компоненты высшего порядка помогли вам упростить ваш код.
Если вам нравится этот пост и нужна помощь с проблемой или искала нанимать фрилансера, я доступен для реагирования, Redux и JavaScript!