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

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

От автора: если вы некоторое время работаете в экосистеме React, есть вероятность, что вы слышали о Higher Order Components — так называются в React компоненты высшего порядка. Давайте рассмотрим простую реализацию, а также попытаемся понять основную идею. Я надеюсь из этой статьи вы получите представление о том, как они работают, и даже как их использовать.

Для чего предназначены Higher-Order Components?

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

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

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

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

Давайте рассмотрим другой пример. Скажем, у вас в приложении есть три JSON-файла. Эти файлы содержат разные данные, которые будут загружены в приложение тремя различными компонентами. Вы хотите дать своим пользователям возможность искать данные, загруженные из этих файлов. Вы можете реализовать функцию поиска во всех трех компонентах. Это дублирование может не вызывать проблем вначале, но по мере роста приложения, все большему количеству компонентов требуется этот функционал, постоянное дублирование будет громоздким и подверженным ошибкам.

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

Как работают компоненты более высокого порядка?

В документации React говорится, что компоненты более высокого порядка принимают компонент и возвращают компонент.

Использование компонентов более высокого порядка пригодится, когда ваша архитектура подготовлена к отделению компонентов контейнера от компонентов презентации. Компонент презентации часто является функциональным компонентом без состояния, который принимает реквизит и отображает пользовательский интерфейс. Функциональными компонентами без состояния являются простые функции JavaScript, которые не имеют состояний. Вот пример:

import React from 'react' const App = ({name}) => { return ( <div> <h2>This is a functional component. Your name is {name}.</h2> </div> )
} ReactDOM.render(<App name='Kingsley' />, document.getElementById("root"));

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

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

Пример Higher-Order Component

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

const hoc = (WrappedComponent) => (props) => { return ( <div> <WrappedComponent {...props}> {props.children.toUpperCase()} </WrappedComponent> </div> )
}

Этот компонент более высокого порядка принимает в качестве аргумента WrappedComponent. Затем он возвращает новый компонент с переданными ему реквизитами, создавая элемент React. Мы вызываем .toUpperCase() для props.children, чтобы преобразовать переданные props.children в верхний регистр.

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

const Username = (props) => ( <div>{props.children}</div>
)

Затем мы оборачиваем Username в компонентом более высокого порядка. Давайте сохраним это в переменной:

const UpperCaseUsername = hoc(Username)

Мы теперь можем использовать его следующим образом в компоненте App:

const App = () => ( <div> <UpperCaseUsername>Kingsley</UpperCaseUsername> </div>
);

Компонент UpperCaseUsername представляет собой просто визуализацию пользовательского интерфейса пользователя, который, в свою очередь, подтягивает состояние из WrappedComponent, выступающего в качестве компонента более высокого порядка.

Более практичный пример Higher-Order Component

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

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

Наш первый компонент загрузит места для пользователей. Мы будем использовать .map() для прокрутки данных, содержащихся в этом файле JSON.

import React from 'react'
// Where the data is located
import preload from './locations.json'
// Manages the data
import LocationCard from './LocationCard' // Renders the presentation of the data
const Location = (props) => { return ( <div> <div> <div> <h2>Preferred Locations</h2> </div> </div> <div> {preload.data .map(location => <LocationCard key={location.id} {...location} />)} </div> </div> )
}
export default Location

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

Теперь нам нужен второй компонент, который в конце также понадобится для поиска. Он будет очень похож на первый компонент, который мы только что создали, но будет иметь другое имя и будет загружать данные из другого места.
Мы хотим, чтобы пользователи могли искать элементы, используя поле ввода. Список элементов, отображаемых в приложении, должен определяться состоянием поиска. Этот функционал будет разделяться между двумя компонентами, над которыми мы работаем. Благодаря идее Higher-Order Component мы можем создать компонент контейнера поиска и обернуть в него другие компоненты.

Назовем компонент withSearch. Этот компонент будет отображать поле ввода для поиска, а также управлять состоянием searchTerm. SearchTerm будет передан в качестве реквизита для обернутого компонента, который будет использоваться для фильтрации извлеченных данных:

import React, { Component } from 'react' const withSearch = (WrappedComponent) => { return class extends Component { state = { searchTerm: '' } handleSearch = event => { this.setState({ searchTerm: event.target.value }) } render() { return ( <div> <div> <input onChange={this.handleSearch} value={this.state.searchTerm} type="text" placeholder="Search" /> </div> <WrappedComponent searchTerm={this.state.searchTerm} /> </div> ) } } }
export default withSearch

Для searchTerm задано состояние пустой строки. Значение, введенное пользователем в поле поиска, получается и используется для установки нового состояния searchTerm. Затем мы передаем searchTerm в WrappedComponent. Мы будем использовать это при фильтрации данных. Чтобы использовать компонент более высокого порядка, нам нужно внести некоторые изменения в компонент представления.

import React, { Component } from 'react'
// Where the data is located
import preload from './locations.json'
// Searches the data
import withSearch from './withSearch
// Manages the data
import LocationCard from './LocationCard' // Renders the presentation of the data
const Location = (props) => { const { searchTerm } = props return ( <div> <div> <div> <h2>Preferred Locations</h2> </div> </div> <div> {preload.data // Filter locations by the inputted search term .filter(location => `${location.name} ${location.zone} ${location.region}`.toUpperCase().indexOf(searchTerm.toUpperCase()) >= 0) // Loop through the locations .map(location => <LocationCard key={location.id} {...location} />)} </div> </div> )
}
export default withSearch(Location)

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

Заключение

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

Автор: Kingsley Silas

Источник: https://css-tricks.com/

Редакция: Команда webformyself.