Понимание HOC в React (на языке Typescript)

HOC расшифровывается как Higher-Order Components.
Это означает компонент, который принимает другой компонент и возвращает улучшенный компонент.

const HoC = Component => EnhancedComponent

Основная цель использования HOC — иметь единое место для обмена функциональными возможностями между компонентами.
(Думайте о HOC скорее как о функции, чем как о компоненте, это будет более понятно по ходу дела).

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

В нашем примере мы просто хотим отображать window.innerWidth в «реальном времени».

Для этого мы можем написать функциональный компонент, использовать хук useEffect и подключить функцию-слушатель, которая будет обновлять переменную состояния.
Смотрите код: https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/ResizeComponent.tsx

Теперь предположим, что нам нужно использовать эту логику в другом компоненте. В этом случае нам придется дублировать нашу логику 😞 (что, очевидно, плохо).

Что может быть лучше (или лучше)?
Мы можем просто абстрагироваться от этого кода useEffect и listener в другой компонент и раскрыть переменную innerWidth как prop.
Этот «специальный» компонент и есть наш компонент высшего порядка.

withResize.tsx

const withResize = (Component: FC<any> )=> (props: any)=> {
    const  [innerWidth, setInnerWidth] = useState(0);

    const handleResize = ()=>{
     setInnerWidth(window.innerWidth);
    }

    useEffect(()=>{
     window.addEventListener('resize', handleResize);

     return ()=>{
       window.removeEventListener('resize', handleResize);
     }
    },[]); 

    return <Component {...props} windowInnerWidth={innerWidth} />
}

Вход в полноэкранный режим Выход из полноэкранного режима

WithResizeUsage.tsx

const WithResizeUsage = ({name, windowInnerWidth}: {name: string, windowInnerWidth: number})=>{
    return (
        <div>Inner Width is {windowInnerWidth} and name is {name}</div>
    )
}

export default withResize(WithResizeUsage);
Вход в полноэкранный режим Выход из полноэкранного режима

App.tsx

  <WithResizeUsage name="Developer" />
Войти в полноэкранный режим Выход из полноэкранного режима

Давайте проанализируем то, что мы написали

  • В App.tsx мы вызываем WithResizeUsage с именем prop.
  • WithResize — это специальная функция, которая принимает Компонент, который сам принимает некоторые реквизиты.
  • Возвратом withResize является компонент со всеми его исходными реквизитами и дополнительным реквизитом windowInnerWidth.
  • В WithResizeUsage: мы принимаем реквизит name, который мы получаем из App.tsx и windowInnerWidth, который мы получаем из withResize hoc.
  • В WithResizeUsage мы оборачиваем его withResize в последней строке withResize(WithResizeUsage), чтобы связать его с withResize.

См. код:
https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/withResize.tsx

https://github.com/abhidatta0/react-hoc-in-typescript/blob/master/src/WithResizeUsage.tsx

Обратите внимание, насколько чистым стал наш компонент WithResizeUsage.

Давайте посмотрим, как мы можем еще немного улучшить HOC.
Предположим, нам нужно, чтобы отображаемая ширина window.innerWidth была на x больше, причем значение x будет настраиваемым. Если внутренняя ширина равна 90, а x равно 3, мы отобразим 93.

Мы изменим HOC, добавив еще один уровень для приема дополнительных параметров. (Эта концепция называется функцией currying).

withResizeAdvanced.tsx

type Params =  {
  bumped: number;
}
const withResizeAdvanced = (params: Params)=> (Component: FC<any> )=> (props: any)=> {
    console.log(params);
    const  [innerWidth, setInnerWidth] = useState(0);

    const handleResize = ()=>{
     setInnerWidth(window.innerWidth+params.bumped);
    }

    useEffect(()=>{
     window.addEventListener('resize', handleResize);

     return ()=>{
       window.removeEventListener('resize', handleResize);
     }
    },[]); 

    return <Component {...props} windowInnerWidth={innerWidth} />
}
Вход в полноэкранный режим Выход из полноэкранного режима

withResizeAdvancedUsage.tsx

import withResizeAdvanced from "./withResizeAdvanced";

const WithResizeUsage = ({name, windowInnerWidth}: {name: string, windowInnerWidth: number})=>{
    return (
        <div>Inner Width is {windowInnerWidth} and name is {name}</div>
    )
}

export default withResizeAdvanced({bumped: 5})(WithResizeUsage);
Вход в полноэкранный режим Выход из полноэкранного режима

App.tsx

      <WithResizeAdvancedUsage name="Developer"/>
Войти в полноэкранный режим Выход из полноэкранного режима

Все аналогично предыдущему HOC, за исключением того, что мы можем передать объект, который имеет свойство bumped, которое мы можем использовать для изменения innerWidth.

Если мы поместим более ранний и новый компонент в App.tsx, то сможем увидеть, что для более позднего компонента innerWidth увеличивается на 5.

Github repo: https://github.com/abhidatta0/react-hoc-in-typescript

Примечание:

  • Убедитесь, что HOC имеет общую логику (например, получение window.innerWidth), которая будет повторно использоваться в других компонентах.
  • Альтернативой шаблону HOC для функционального компонента является использование пользовательских хуков.

Оцените статью
devanswers.ru
Добавить комментарий