Что такое render props в React?

MiddleReact · Frontend·Обновлено 10 августа 2026
Коротко
Render props — паттерн, при котором компонент принимает функцию в качестве пропа и вызывает её при рендеринге, передавая внутреннее состояние как аргумент. Это позволяет переиспользовать логику компонентов без наследования.

Render Props

Render props — это паттерн проектирования в React, при котором компонент принимает проп, значением которого является функция, возвращающая React-элемент. Компонент вызывает эту функцию вместо реализации собственной логики рендеринга, передавая ей своё внутреннее состояние или методы.

Зачем нужен этот паттерн

Основная цель — переиспользование логики с состоянием между разными компонентами без дублирования кода. До появления хуков это был один из главных способов решить проблему совместного использования поведения (наряду с HOC). Паттерн позволяет компоненту-«провайдеру» управлять данными, а потребителю — контролировать отображение.

Как это работает

Компонент с render prop инкапсулирует некую логику (например, отслеживание позиции мыши, статус загрузки, данные формы) и предоставляет её наружу через вызов переданной функции.

// Провайдер: управляет позицией мыши
const MouseTracker = ({ render }: { render: (pos: { x: number; y: number }) => React.ReactNode }) => {
  const [pos, setPos] = useState({ x: 0, y: 0 });

  return (
    <div
      style={{ height: '100vh' }}
      onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}
    >
      {render(pos)}
    </div>
  );
};

// Потребитель: решает, как отображать данные
const App = () => (
  <MouseTracker
    render={({ x, y }) => <p>Позиция курсора: {x}, {y}</p>}
  />
);

Вариант через children как функцию

Вместо именованного пропа render часто используют children как функцию — это называется «children as a function» и является частным случаем того же паттерна:

const DataFetcher = ({ url, children }: { url: string; children: (data: any, loading: boolean) => React.ReactNode }) => {
  const [data, setData] = useState(null);
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    fetch(url)
      .then((res) => res.json())
      .then((json) => {
        setData(json);
        setLoading(false);
      });
  }, [url]);

  return <>{children(data, loading)}</>;
};

// Использование
const UserProfile = () => (
  <DataFetcher url="/api/user">
    {(data, loading) => loading ? <Spinner /> : <div>{data?.name}</div>}
  </DataFetcher>
);

Render Props vs Hooks

С появлением хуков большинство задач, решаемых через render props, теперь элегантнее решаются через custom hooks:

// Тот же MouseTracker, но как хук
const useMousePosition = () => {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  const handleMouseMove = (e: MouseEvent) => setPos({ x: e.clientX, y: e.clientY });

  useEffect(() => {
    window.addEventListener('mousemove', handleMouseMove);
    return () => window.removeEventListener('mousemove', handleMouseMove);
  }, []);

  return pos;
};

Когда render props ещё актуальны

  • В библиотеках с широкой поддержкой React < 16.8 (Formik, React Router v5, Downshift)
  • Когда логика неотделима от JSX-контекста рендеринга
  • Когда нужно явно передавать рендер в слот (compound components, scoped slots)
  • В библиотеках компонентов для максимальной гибкости UI

Что хочет услышать интервьюер

Понимание сути паттерна: компонент передаёт своё состояние через вызов функции-пропа

Умение привести рабочий пример кода с render prop или children as a function

Знание главной цели — переиспользование логики с состоянием

Понимание разницы между render props и HOC, и почему хуки вытеснили оба паттерна

Упоминание реальных библиотек, использующих этот паттерн (Formik, React Router, Downshift)

Пример: Базовый пример render prop с отслеживанием мыши

import React, { useState } from 'react';

interface MousePosition {
  x: number;
  y: number;
}

interface MouseTrackerProps {
  render: (position: MousePosition) => React.ReactNode;
}

// Компонент инкапсулирует логику, делегирует рендеринг
const MouseTracker: React.FC<MouseTrackerProps> = ({ render }) => {
  const [position, setPosition] = useState<MousePosition>({ x: 0, y: 0 });

  const handleMouseMove = (e: React.MouseEvent) => {
    setPosition({ x: e.clientX, y: e.clientY });
  };

  return (
    <div style={{ height: '100vh' }} onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
};

// Потребитель управляет отображением
const App: React.FC = () => (
  <MouseTracker
    render={({ x, y }) => (
      <h2>Позиция курсора: X={x}, Y={y}</h2>
    )}
  />
);

export default App;

Пример: Children as a function — загрузка данных

import React, { useState, useEffect } from 'react';

interface FetcherProps<T> {
  url: string;
  children: (data: T | null, loading: boolean, error: string | null) => React.ReactNode;
}

// Универсальный компонент для загрузки данных
function Fetcher<T>({ url, children }: FetcherProps<T>) {
  const [data, setData] = useState<T | null>(null);
  const [loading, setLoading] = useState(true);
  const [error, setError] = useState<string | null>(null);

  useEffect(() => {
    setLoading(true);
    fetch(url)
      .then((res) => {
        if (!res.ok) throw new Error('Ошибка сети');
        return res.json();
      })
      .then((json: T) => setData(json))
      .catch((err: Error) => setError(err.message))
      .finally(() => setLoading(false));
  }, [url]);

  // Вызываем children как функцию, передавая внутреннее состояние
  return <>{children(data, loading, error)}</>;
}

interface User {
  id: number;
  name: string;
}

const UserList: React.FC = () => (
  <Fetcher<User[]> url="/api/users">
    {(users, loading, error) => {
      if (loading) return <p>Загрузка...</p>;
      if (error) return <p>Ошибка: {error}</p>;
      return (
        <ul>
          {users?.map((u) => <li key={u.id}>{u.name}</li>)}
        </ul>
      );
    }}
  </Fetcher>
);

Типичные ошибки

Путают render props с обычной передачей компонента как пропа — не понимают, что ключевое слово «функция, которая вызывается»

Не знают про вариант children as a function и думают, что render prop — это строго проп с именем render

Не могут объяснить проблему производительности: при каждом рендере родителя создаётся новая функция, что мешает PureComponent/memo

Считают паттерн устаревшим и не знают, в каких случаях он до сих пор уместен

Путают render props с HOC, не могут чётко объяснить отличия в структуре и используемых компонентах

Лучшие курсы по теме

изображение курса

TypeScript с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.8
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Feature-Sliced Design

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.6
3 999 ₽ 6 990 ₽
Подробнее
изображение курса

Next.js - с нуля

Антон Ларичев
AI-тренажерыAI-тренажеры
Практика в студииПрактика в студии
Гарантия
Бонусы
иконка звёздочки рейтинга4.7
3 999 ₽ 6 990 ₽
Подробнее