JavaRush /Курсы /Модуль 3: React /Типизация HOC с использованием интерфейсов

Типизация HOC с использованием интерфейсов

Модуль 3: React
4 уровень , 4 лекция
Открыта

Основы типизации HOC с TypeScript

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

Для начала, освежим базовый HOC без типизации:

import React from 'react';

// Небольшой HOC, который добавляет логику загрузки
const withLoading = (Component: React.ComponentType) => {
  return (props: any) => {
    const [isLoading, setIsLoading] = React.useState(true);

    React.useEffect(() => {
      setTimeout(() => setIsLoading(false), 3000);
    }, []);

    return isLoading ? <p>Loading...</p> : <Component {...props} />;
  };
};

Этот HOC добавляет индикатор загрузки при рендеринге компонента. Но здесь полно «магии» (читай: props: any) и полное отсутствие типизации! Исправим это.

Типизация базового HOC

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

Компонент, который оборачивается HOC, называется WrappedComponent. Его пропсы можно типизировать так:

type WrappedComponentProps = {
  title: string;
};

Теперь передадим этот тип в HOC:

import React from 'react';

const withLoading = <P extends object>(Component: React.ComponentType<P>) => {
  return (props: P) => {
    const [isLoading, setIsLoading] = React.useState(true);

    React.useEffect(() => {
      setTimeout(() => setIsLoading(false), 3000);
    }, []);

    return isLoading ? <p>Loading...</p> : <Component {...props} />;
  };
};

Разберём <P extends object>

  • <P> — это обобщённый (generic) тип, который позволяет передавать тип пропсов WrappedComponent.
  • extends object ограничивает, что P должен быть объектом. Это защитит вас от передачи, например, строки или числа вместо пропсов.

Теперь HOC принимает только те пропсы, которые передаются в WrappedComponent.

Пример использования:

const MyComponent: React.FC<WrappedComponentProps> = ({ title }) => {
  return <h1>{title}</h1>;
};

// Оборачиваем HOC
const EnhancedComponent = withLoading(MyComponent);

// Используем обёрнутый компонент
<EnhancedComponent title="Hello, HOC!" />;

Как добавить пропсы в HOC?

Теперь добавим собственные пропсы в HOC. Например, мы хотим передать проп message для настройки текста при загрузке. Для этого HOC должен принимать дополнительные пропсы.

Шаг 1: Создаём интерфейс для HOC-пропсов

interface WithLoadingProps {
  message?: string;
}

Шаг 2: Расширяем типы пропсов HOC

Теперь мы можем объединить WithLoadingProps с P:

const withLoading = <P extends object>(
  Component: React.ComponentType<P>
) => {
  return ({ message, ...props }: WithLoadingProps & P) => {
    const [isLoading, setIsLoading] = React.useState(true);

    React.useEffect(() => {
      setTimeout(() => setIsLoading(false), 3000);
    }, []);

    return isLoading ? <p>{message || 'Loading...'}</p> : <Component {...(props as P)} />;
  };
};

Всё вместе

const MyComponent: React.FC<WrappedComponentProps> = ({ title }) => {
  return <h1>{title}</h1>;
};

// Оборачиваем компонент с новым пропом
const EnhancedComponent = withLoading(MyComponent);

// Используем HOC с дополнительным пропсом
<EnhancedComponent title="Hello, TypeScript!" message="Please wait, data is loading..." />;

Углубляемся в типизацию: React.ComponentType

Вы, наверное, заметили, что мы используем React.ComponentType<P>. Это универсальный тип в React, который покрывает как функциональные, так и классовые компоненты. Если вы попытаетесь использовать вместо него React.FC<P>, вы получите ошибки при оборачивании классовых компонентов.

Сравните:

import React from 'react';

// Используем React.FC — будет работать только с функциональными компонентами
const withLoadingFC = <P extends object>(Component: React.FC<P>) => {
  return (props: P) => <Component {...props} />;
};

// Используем React.ComponentType — универсальный подход
const withLoadingCT = <P extends object>(Component: React.ComponentType<P>) => {
  return (props: P) => <Component {...props} />;
};

Типизация сложных HOC с помощью интерфейсов

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

Шаг 1: Интерфейс для HOC-пропсов

Создадим интерфейс, который определяет, что компонент может получать проп isAuthenticated:

interface WithAuthProps {
  isAuthenticated: boolean;
}

Шаг 2: Реализация HOC

const withAuth = <P extends object>(
  Component: React.ComponentType<P>
) => {
  return (props: WithAuthProps & P) => {
    if (!props.isAuthenticated) {
      return <p>You must be logged in to view this content.</p>;
    }

    return <Component {...(props as P)} />;
  };
};

Пример использования:

const Dashboard: React.FC = () => {
  return <h1>Welcome to the Dashboard!</h1>;
};

const ProtectedDashboard = withAuth(Dashboard);

// Используем HOC
<ProtectedDashboard isAuthenticated={true} />;
<ProtectedDashboard isAuthenticated={false} />;

Типизация HOC, обрабатывающего разные типы пропсов

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

Рассмотрим HOC, который добавляет обязательный проп timestamp:

interface WithTimestampProps {
  timestamp: number;
}

const withTimestamp = <P extends object>(
  Component: React.ComponentType<P & WithTimestampProps>
) => {
  return (props: P) => {
    const timestamp = Date.now();
    return <Component {...props} timestamp={timestamp} />;
  };
};

// Используем HOC
const MyComponent: React.FC<{ name: string } & WithTimestampProps> = ({ name, timestamp }) => {
  return (
    <div>
      <p>Name: {name}</p>
      <p>Timestamp: {timestamp}</p>
    </div>
  );
};

const EnhancedComponent = withTimestamp(MyComponent);

<EnhancedComponent name="John Doe" />;
1
Задача
Модуль 3: React, 4 уровень, 4 лекция
Недоступна
Базовая типизация HOC
Базовая типизация HOC
1
Задача
Модуль 3: React, 4 уровень, 4 лекция
Недоступна
HOC с пользовательскими пропсами
HOC с пользовательскими пропсами
3
Опрос
Компоненты высшего порядка, 4 уровень, 4 лекция
Недоступен
Компоненты высшего порядка
Компоненты высшего порядка
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ