JavaRush /Курсы /Модуль 3: React /Создание компонента с рендер-пропсами для предоставления ...

Создание компонента с рендер-пропсами для предоставления данных

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

Когда использовать рендер-пропсы?

Рендер-пропсы особенно хороши, когда вам нужно разделить логику и отображение. Например: - Работать с API, получая данные и предоставляя их компонентам. - Управлять состояниями, такими как модальные окна, меню, переключатели.

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

Пример №1: создание компонента с рендер-пропсами

Для начала создадим простой компонент, который будет передавать данные через рендер-пропсы. Наш компонент называется DataProvider. Он предоставляет данные через переданный рендер-пропс.

import React, { ReactNode } from "react";

// Создадим интерфейс для данных
interface DataProviderProps {
  render: (data: string[]) => ReactNode; // Рендер-пропс — функция, возвращающая ReactNode
}

const DataProvider: React.FC<DataProviderProps> = ({ render }) => {
  // Данные, которыми мы будем делиться
  const data = ["🍎 Apple", "🍊 Orange", "🍌 Banana"];

  return (
    <div>
      <h3>Here is your data:</h3>
      {render(data)} {/* Вызываем пропс render и передаем ему данные */}
    </div>
  );
};

export default DataProvider;

И теперь мы можем использовать этот компонент:

import React from "react";
import DataProvider from "./DataProvider";

const App: React.FC = () => {
  return (
    <DataProvider
            render={(data) => (
        <ul>
          {data.map((item, index) => (
            <li key={index}>{item}</li>
          ))}
        </ul>
      )}
    />
  );
};

export default App;

Компонент DataProvider предоставляет данные через функцию, а App решает, как их отобразить. Это гибкость в чистом виде.

Пример №2: управление состоянием через рендер-пропсы

Теперь давайте создадим компонент Toggle, который будет управлять состоянием (вкл./выкл.) и предоставлять его через рендер-пропс.

import React, { useState, ReactNode } from "react";

interface ToggleProps {
  render: (isOn: boolean, toggle: () => void) => ReactNode;
}

const Toggle: React.FC<ToggleProps> = ({ render }) => {
  const [isOn, setIsOn] = useState(false);

  const toggle = () => setIsOn(!isOn); // Переключаем состояние

  return <div>{render(isOn, toggle)}</div>; // Передаём состояние и функцию toggle
};

export default Toggle;

Используем его:

import React from "react";
import Toggle from "./Toggle";

const App: React.FC = () => {
  return (
    <Toggle
            render={(isOn, toggle) => (
        <div>
          <p>The toggle is {isOn ? "ON" : "OFF"}</p>
          <button onClick={toggle}>Switch</button>
        </div>
      )}
    />
  );
};

export default App;

Теперь у нас есть компонент, который управляет своим состоянием, но оставляет рендеринг полностью на усмотрение потребителя. Хотите сделать переключатель в виде кнопки? Пожалуйста! Хотите заменить это на чекбокс? Без проблем.

Как типизировать рендер-пропсы?

Для типизации рендер-пропсов нам нужно описать функцию, которая передаётся как пропс. Вот несколько примеров.

Пример 1: рендер-пропс с простыми данными

Как в случае с DataProvider, мы передаём функцию, принимающую массив строк:

interface DataProviderProps {
  render: (data: string[]) => ReactNode; // Функция принимает массив строк, возвращает ReactNode
}

Пример 2: рендер-пропс с состоянием

Как в случае с Toggle, функция принимает состояние и функцию для его изменения:

interface ToggleProps {
  render: (isOn: boolean, toggle: () => void) => ReactNode; // Состояние и функция toggle
}

Пример №3: используем рендер-пропсы для работы с API

Давайте создадим компонент FetchData, который будет загружать данные с API и отдавать их через рендер-пропс.

import React, { useState, useEffect, ReactNode } from "react";

interface FetchDataProps {
  url: string; // URL для загрузки данных
  render: (data: any | null, loading: boolean, error: string | null) => ReactNode;
}

const FetchData: React.FC<FetchDataProps> = ({ url, render }) => {
  const [data, setData] = useState<any | nullм(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    fetch(url)
      .then((response) => response.json())
      .then((data) => {
        setData(data);
        setLoading(false);
      })
      .catch((error) => {
        setError(error.message);
        setLoading(false);
      });
  }, [url]);

  return <div>{render(data, loading, error)}</div>;
};

export default FetchData;

Используем его:

import React from "react";
import FetchData from "./FetchData";

const App: React.FC = () => {
  return (
    <FetchData
            url="https://jsonplaceholder.typicode.com/todos"
            render={(data, loading, error) => {
        if (loading) return <p>Loading...</p>;
        if (error) return <p>Error: {error}</p>;

        return (
          <ul>
            {data.map((todo: any) => (
              <li key={todo.id}>{todo.title}</li>
            ))}
          </ul>
        );
      }}
    />
  );
};

export default App;

Этот компонент загружает данные с API и предоставляет их через рендер-пропсы. Мы можем использовать эту логику где угодно, не дублируя код.

Типичные ошибки при работе с рендер-пропсами

Иногда рендер-пропсы могут быть слишком переусложнёнными, если использовать их избыточно. Если компонент слишком универсален, он может стать трудно читаемым. Используйте рендер-пропсы, когда это действительно нужно. Если задача проще, возможно, лучше подойдут обычные пропсы или HOC.

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