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

Типизация функций рендер-пропсов

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

Базовый пример рендер-пропса

type RenderProps = {
  data: string;
};

const DataProvider: React.FC<{ render: (props: RenderProps) => JSX.Element }> = ({ render }) => {
  const someData = "Hello from DataProvider!";
  return <>{render({ data: someData })}</>;
};

const App = () => {
  return (
    <DataProvider
            render={({ data }) => <div>{data}</div>}
    />
  );
};

В этом примере мы передали функцию render через пропс. Она принимает объект { data } и возвращает JSX. Всё просто, но без типизации это могла бы быть неразбериха вроде any. Представьте, что вы случайно передадите не тот тип данных в data. Упс.

Типизация функции рендер-пропса

Теперь давайте разберёмся, как правильно типизировать функции рендер-пропсов.

1. Описание типа пропса

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

type RenderProps = {
  data: string;
};

Тут мы говорим TypeScript: «Эй, рендер-функция примет объект с полем data, и это поле будет строкой».

2. Типизация компонента

На следующем этапе нужно сообщить компоненту, что он принимает функцию в качестве пропса.

const DataProvider: React.FC<{
  render: (props: RenderProps) => JSX.Element;
}> = ({ render }) => {
  const someData = "Hello from DataProvider!";
  return <>{render({ data: someData })}</>;
};

Здесь render — это функция, которая принимает объект типа RenderProps и возвращает JSX. Обратите внимание: это важно, чтобы TypeScript мог проверить корректность у переданной функции.

3. Использование компонента

Шаг простой, но, чтобы TypeScript подхватил всю типизацию правильно, сам вызывающий код также должен быть корректен:

const App = () => {
  return (
    <DataProvider
      render={({ data }) => <div>{data}</div>}
    />
  );
};

Если вы случайно попытаетесь сделать что-то не то, например:

<DataProvider
  render={(value) => <div>{value}</div>} // ошибка
/>

TypeScript сразу подскажет, что value не соответствует типу RenderProps. Всё под контролем!

Задачи посложнее: когда рендер-пропсы зависят от типов

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

Пример: типизация рендер-пропсов для списков

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

type ListProps<T> = {
  items: T[];
  render: (item: T) => JSX.Element;
};

Этот интерфейс говорит: «Я приму массив любого типа T и рендер-функцию, которая обработает каждый элемент этого массива».

Теперь создаём компонент:

const List = <T extends unknown>({ items, render }: ListProps<T>) => {
  return (
    <ul>
      {items.map((item, index) => (
        <li key={index}>{render(item)}</li>
      ))}
    </ul>
  );
};

Обратите внимание на <T extends unknown>: здесь T — это обобщённый тип. Он говорит TypeScript, что List может работать с любым типом.

Использование компонента

Теперь посмотрим, как использовать такой компонент. Для примера возьмём массив строк:

const App = () => {
  const strings = ["React", "TypeScript", "HOC"];
  return (
    <List
      items={strings}
      render={(item) => <strong>{item}</strong>}
    />
  );
};

TypeScript автоматически понимает, что item — это строка, и вы получите ошибку, если попробуете, например, применить к нему числовую операцию.

Хотим использовать числа? Пожалуйста:

const numbers = [1, 2, 3, 4, 5];
return (
  <List
    items={numbers}
    render={(item) => <em>{item * 2}</em>}
  />
);

Разбор типичных ошибок и ловушек

Неверная типизация рендер-функции

Если вы неправильно укажете тип рендер-пропса, TypeScript начнёт выдавать ошибки. Например:

type RenderProps = {
  data: string;
};

const DataProvider: React.FC<{ render: (props: RenderProps) => JSX.Element }> = ({ render }) => {
  const someData = "Hello!";
  return <>{render({ data: someData })}</>;
};

// Неверная типизация:
render={(props) => <div>{props.number}</div>} // Ошибка: 'number' не существует в типе 'RenderProps'.

Практическое применение

В реальных проектах рендер-пропсы часто используются для:

  1. Общих компонентов: рендер-пропсы позволяют удобно делиться логикой, например, отображением списков или загрузкой данных.
  2. Обеспечения гибкости: с их помощью можно легко передавать уникальную JSX-разметку в общий компонент.
  3. Типизация API-данных: через рендер-пропсы можно типизировать данные, которые компонент забирает из API, что предотвращает ошибки.

На этом всё! Теперь вы знаете, как типизировать функции рендер-пропсов и использовать их в реальных проектах. В следующей лекции мы сравним HOC и рендер-пропсы и разберёмся, в каких случаях выбирать тот или иной подход.

1
Задача
Модуль 3: React, 4 уровень, 7 лекция
Недоступна
Базовая реализация рендер-пропса
Базовая реализация рендер-пропса
1
Задача
Модуль 3: React, 4 уровень, 7 лекция
Недоступна
Рендер-пропс с типизированным массивом
Рендер-пропс с типизированным массивом
Комментарии
ЧТОБЫ ПОСМОТРЕТЬ ВСЕ КОММЕНТАРИИ ИЛИ ОСТАВИТЬ КОММЕНТАРИЙ,
ПЕРЕЙДИТЕ В ПОЛНУЮ ВЕРСИЮ