← Назад к вопросам

Что такое Render Props?

1.8 Middle🔥 91 комментариев
#React#Архитектура и паттерны

Комментарии (1)

🐱
claude-haiku-4.5PrepBro AI2 апр. 2026 г.(ред.)

Ответ сгенерирован нейросетью и может содержать ошибки

Что такое Render Props?

Render Props - это паттерн проектирования в React, который позволяет компонентам делиться кодом через функцию в качестве prop. Вместо передачи предварительно отрендеренного JSX, компонент получает функцию, которая сам решает, как рендерить содержимое.

Основная идея

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

// Компонент с логикой, использующий render prop
function Mouse({ render }) {
  const [position, setPosition] = React.useState({ x: 0, y: 0 });

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

  return (
    <div onMouseMove={handleMouseMove}>
      {render(position)}
    </div>
  );
}

// Использование
function App() {
  return (
    <Mouse
      render={({ x, y }) => (
        <div>
          <h1>Координаты мыши</h1>
          <p>X: {x}, Y: {y}</p>
        </div>
      )}
    />
  );
}

Почему это полезно?

1. Переиспользуемость логики

Одна и та же логика (отслеживание позиции мыши) может быть использована для разных визуальных представлений без дублирования кода:

// Первый способ отображения
<Mouse
  render={({ x, y }) => (
    <div>X: {x}, Y: {y}</div>
  )}
/>

// Второй способ отображения
<Mouse
  render={({ x, y }) => (
    <circle cx={x} cy={y} r="5" />
  )}
/>

2. Разделение ответственности

Компонент с логикой не знает и не заботится о том, как данные будут отображены. Это делает код более гибким и тестируемым.

3. Реактивное обновление

Когда данные изменяются, функция-prop вызывается заново с новыми значениями, обновляя отрендеренный результат.

Реальный пример: Form validation

function FormValidator({ onSubmit, children }) {
  const [values, setValues] = React.useState({ email: '', password: '' });
  const [errors, setErrors] = React.useState({});

  const handleChange = (e) => {
    const { name, value } = e.target;
    setValues(prev => ({ ...prev, [name]: value }));
  };

  const handleSubmit = (e) => {
    e.preventDefault();
    const newErrors = {};
    
    if (!values.email.includes('@')) newErrors.email = 'Invalid email';
    if (values.password.length < 6) newErrors.password = 'Too short';
    
    setErrors(newErrors);
    if (Object.keys(newErrors).length === 0) onSubmit(values);
  };

  return children({
    values,
    errors,
    handleChange,
    handleSubmit
  });
}

// Использование
function LoginForm() {
  return (
    <FormValidator onSubmit={(data) => console.log(data)}>
      {({ values, errors, handleChange, handleSubmit }) => (
        <form onSubmit={handleSubmit}>
          <input
            name="email"
            value={values.email}
            onChange={handleChange}
          />
          {errors.email && <span>{errors.email}</span>}
          
          <input
            name="password"
            type="password"
            value={values.password}
            onChange={handleChange}
          />
          {errors.password && <span>{errors.password}</span>}
          
          <button type="submit">Login</button>
        </form>
      )}
    </FormValidator>
  );
}

Отличие от HOC (Higher-Order Components)

Render Props и HOC решают одну проблему - переиспользование логики компонентов, но разными способами:

// HOC подход
const EnhancedComponent = withMouse(MyComponent);

// Render Props подход
<Mouse render={data => <MyComponent {...data} />} />

Render Props более явны и понятны, так как видно, какие данные передаются в функцию. HOC скрывает эту информацию.

Варианты синтаксиса

1. Prop render

<Mouse render={({ x, y }) => <div>{x}, {y}</div>} />

2. Prop children (функция)

<Mouse>
  {({ x, y }) => <div>{x}, {y}</div>}
</Mouse>

3. Несколько функций-props

<Component
  renderHeader={() => <header>...</header>}
  renderContent={(data) => <main>{data}</main>}
  renderFooter={() => <footer>...</footer>}
/>

Проблемы и ограничения

1. Производительность

Если функция-prop не мемоизирована, компонент будет переренден на каждой итерации:

// Плохо - новая функция на каждый рендер
<Mouse render={({ x, y }) => <div>{x}, {y}</div>} />

// Хорошо - мемоизированная функция
const renderPosition = React.useCallback(
  ({ x, y }) => <div>{x}, {y}</div>,
  []
);
<Mouse render={renderPosition} />

2. Читаемость

Глубокая вложенность функций может снизить читаемость кода.

Современная альтернатива: Custom Hooks

С появлением Hooks, Render Props постепенно заменяются на Custom Hooks, которые проще и понятнее:

// Custom Hook вместо Render Props
function useMouse() {
  const [position, setPosition] = React.useState({ x: 0, y: 0 });
  const [ref, setRef] = React.useState(null);

  React.useEffect(() => {
    if (!ref) return;
    const handleMouseMove = (event) => {
      setPosition({ x: event.clientX, y: event.clientY });
    };
    ref.addEventListener('mousemove', handleMouseMove);
    return () => ref.removeEventListener('mousemove', handleMouseMove);
  }, [ref]);

  return { position, ref };
}

// Использование
function App() {
  const { position, ref } = useMouse();
  return (
    <div ref={ref}>
      <p>X: {position.x}, Y: {position.y}</p>
    </div>
  );
}

Заключение

Render Props - это мощный паттерн для переиспользования логики в React, особенно до появления Hooks. Его основная ценность в том, что он явно показывает, как данные передаются в отрендеренный контент. Однако в современном React предпочтительнее использовать Custom Hooks, так как они проще и более удобны в использовании.