Пока знакомился с HOC (High Order Component) в React, обнаружил, что все тематические видео на ютубе делятся на 2 категории: невероятное старье, не содержат типизации.
Согласен, что есть "замечательная" статья на reactdev, написанная абсолютно нечеловекочитаемым языком, и еще одна статья из архива reactdev, которая выглядит чуть лучше, но я все же попробую передать более дружелюбный пример с участием typescript и функциональных компонентов.
Немного описания
HOC - паттерн, используемый во фреймворках по типу React, Vue и т.д. для создания компонента высшего порядка, объединяющего логику других компонентов, имеющих схожую функциональность
Принято называть HOC-функции с with (withSubscription) и располагать в отдельной папке hocs.
HOC-функция принимает в качестве аргумента исходный компонент и оборачивает его необходимыми свойствами и функционалом
Немного кода
Имеем несколько интерфейсов:
{/* собственные пропсы оборачиваемого компонента */}
export interface PersonalProps {
isEnabled: boolean
}
{/* пропсы, генерируемые HOC-компонентом и дополняющие пропсы оборачиваемого компонента */}
export interface WrapperGeneratedProps {
onButtonClick: () => void
buttonText: string
}
{/* пропсы используемые только внутри HOC-компонента */}
export interface WrapperProps {
alertText: string
}
Возьмем в качестве компонента обычную кнопку, которая получит handler, текст и статус enabled/disabled:
//Button.tsx
const Button = ({isEnabled, onButtonClick, buttonText}: PersonalProps & WrapperGeneratedProps) => {
return <button disabled={isEnabled} onClick={onButtonClick}>{buttonText}</button>
}
Напишем HOC:
//withClick.tsx
export function withClick(WrappedComponent: ComponentType<PersonalProps & WrapperGeneratedProps>): ComponentType<PersonalProps & WrapperProps>{
return function(props: PersonalProps & WrapperProps): ReactElement {
{/* здесь может находиться любой функционал, который является общим для всех компонентов оборачиваемых в HOC */}
const onButtonClick = () => {
alert(props.alertText)
}
const newProps = {onButtonClick, buttonText: 'Button Text', ...props}
return <WrappedComponent {...newProps} />
}
}
Важное замечание: при использовании спред-синтаксис лучше расположить его после всех генерируемых HOC-компонентом пропсов, чтобы не затерлись исходные передаваемые пропсы
Теперь вызов HOC внутри компонента, например в App
//App.tsx
{/* передаем оборачиваемый компонент в HOC*/}
const WithClickButton = withClick(Button)
const [isEnabled, setIsEnabled] = useState(false)
return (
{/* передаем пропсы базового компонента и пропсы используемые только внутри HOC-компонента */}
<WithClickButton isEnabled={isEnabled} alertText="Some Text"/>
)
Вот и все. Ничего сложного.