Higher Order Components (HOC) - компоненты высшего порядка. Это паттерн React призванный сократить дублирование логики.
Пусть слово компонент не вводит в заблуждение, на самом деле - это функция которая возвращает компонент. За основу HOC взят паттерн Функция высшего порядка.
Фукнции высшего порядка в Javascript мы используем довольно часто. Это наши любимые .map(), reduce(), filter() и тд. То есть функции, которые в качестве аргумента принимают другие функции.
Компоненты высшего порядка очень похожи, только вместо функции, аргументом они принимают компонент. И мы, как бы, возвращаем его "прокаченную" версию. В React с HOC можно столкнуться работая с такими библиотеками как Redux или Material UI.
Допустим у нас есть кнопка Button.js, которая меняет цвет по клику
Button.js
import React, { Component } from 'react'
export default class Button extends Component {
constructor(props) {
super(props)
this.state = {
color: '#bada55'
}
}
changeColor = () =>{
const color = `#${Math.floor(Math.random()*16777215).toString(16)}`;
this.setState({
color
})
}
render() {
const styles = {
padding:'10px',
border:'none',
cursor:'pointer',
margin:'15px',
background:this.state.color}
return (
<button onClick={this.changeColor} style={styles}>Change color </button>
)
}
}
Все круто. А теперь представим, что нам нужен еще компонет Box.js в котором будет использоваться та же логика, только вместо клика на кнопку, цвет будет меняться при наведении курсора на компонет.
Box.js
import React, {Component } from 'react'
export default class Box extends Component{
constructor(props) {
super(props)
this.state = {
color: '#bada55'
}
}
changeColor = () =>{
const color = `#${Math.floor(Math.random()*16777215).toString(16)}`;
this.setState({
color
})
}
render() {
const styles = {width:'200px',
height:'200px',
margin:'15px',
background:this.state.color}
return (
<div onMouseOver={this.changeColor} style={styles}>
Hover to change color
</div>
)
}
}
Всё хорошо, но теперь у нас дублируется код. В нашем примере, в этом нет ничего плохого, 1 простая функция и компонентов всего 2, но что если нужно будет создать еще компоненты с такой же логикой и что если функций будет больше?
Чтобы не копировать код каждый раз и избежать повторов( принцип DRY Don't Repeat Yourself ), создадим компонент высшего порядка withColor.js и перенесем логику туда:
withColor.js
import React, {Component} from 'react'
const withColor = (WrappedComponent) =>{
return class extends Component{
state = {
color: '#bada55'
}
changeColor = () =>{
const color = `#${Math.floor(Math.random()*16777215).toString(16)}`;
this.setState({
color
})
}
render(){
return (
<WrappedComponent changeColor={this.changeColor} color={this.state.color} />
)
}
}
}
export default withColor;
Мы перенесли стейт и функцию changeColor из Button.js и Box.js в HOC withColor, который принимает в качестве аргумента компонент и возвращает его с пропсами. Теперь можно переписать Button.js и Box.js и экспортировать их с оберткой в компонент высшего порядка который мы создали.
Button.js
import withColor from './withColor'
import React from 'react'
function Button(props) {
const styles = {
padding:'10px',
border:'none',
cursor:'pointer',
margin:'15px',
background:props.color
}
return (
<button onClick={props.changeColor} style={styles} >Change color </button>
)
}
export default withColor(Button)
Box.js
import React from 'react'
import withColor from './withColor';
const Box = (props) =>{
const styles = {
width:'200px',
height:'200px',
margin:'15px',
background:props.color
}
return (
<div onMouseOver={props.changeColor} style={styles}>
Hover to change color
</div>
)
}
export default withColor(Box);
Теперь наши компоненты Button и Box стали проще. Функция changeColor и state вынесены в HOC и если нам придется добавить еще компонент с такой же логикой, нам не нужно будет дублировать код.
Пример далекий от практики, но главная идея должна быть понятна.
Собственно в этом и вся суть компонентов высшего порядка. Лично мне почти не приходилось использовать этот паттерн и, с введением хуков в React, юзкейсов для HOC стало меньше, но всё же это полезно знать.