Render Props и Function as Children

Render Props — это паттерн в разработке компонентов, позволяющий передавать функцию в качестве свойства, которая управляет тем, как компонент будет визуализирован. В контексте Preact этот подход особенно полезен для создания высоко переиспользуемых и композиционно гибких компонентов.

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

Вместо того чтобы компонент жёстко определял свой UI, он делегирует часть ответственности вызывающему коду через функцию. Эта функция, получая внутреннее состояние компонента или данные, возвращает элементы JSX.

Пример простого компонента с render prop:

import { h, Component } from 'preact';

class MouseTracker extends Component {
  state = { x: 0, y: 0 };

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

  render() {
    return (
      <div onMouseM ove={this.handleMouseMove} style={{ height: '100vh' }}>
        {this.props.render(this.state)}
      </div>
    );
  }
}

// Использование:
<MouseTracker render={({ x, y }) => (
  <p>Координаты мыши: {x}, {y}</p>
)} />

Ключевые моменты:

  • render — это функция, переданная как проп.
  • Компонент MouseTracker не знает, как именно будут отображаться данные, он лишь предоставляет их через state.
  • Такой подход позволяет повторно использовать логику компонента без дублирования UI-кода.

Function as Children

Функция в качестве дочернего элемента (children) является альтернативой render props. Она полностью идентична концептуально, но синтаксически выглядит более лаконично:

<MouseTracker>
  {({ x, y }) => <p>Координаты мыши: {x}, {y}</p>}
</MouseTracker>

Внутри компонента MouseTracker нужно просто рендерить this.props.children:

render() {
  return (
    <div onMouseM ove={this.handleMouseMove} style={{ height: '100vh' }}>
      {this.props.children(this.state)}
    </div>
  );
}

Преимущества Function as Children:

  • Более читаемый и компактный синтаксис.
  • Избегает лишних пропов, таких как render.
  • Отлично работает с композициями компонентов и вложенными функциями.

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

  1. Управление состоянием: Можно создавать компоненты, которые инкапсулируют сложное состояние, передавая его UI-функции.
  2. Повторное использование логики: Например, компонент для работы с формами, который предоставляет значения полей и методы для их обновления, но сам UI оставляет вызывающему коду.
  3. Композиция с другими компонентами: Render Props позволяют легко объединять несколько источников состояния без жесткой привязки к конкретному виду интерфейса.

Взаимодействие с хуками Preact

С приходом Preact Hooks использование Render Props стало ещё гибче. Пример функционального компонента с хуком:

import { h } from 'preact';
import { useState, useEffect } from 'preact/hooks';

function MouseTracker({ children }) {
  const [coords, setCoords] = useState({ x: 0, y: 0 });

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

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

  return children(coords);
}

// Использование:
<MouseTracker>
  {({ x, y }) => <p>Координаты мыши: {x}, {y}</p>}
</MouseTracker>

Особенности при использовании хуков:

  • Упрощает управление побочными эффектами (useEffect) и состоянием (useState).
  • Логика остается полностью переиспользуемой и изолированной от UI.
  • Позволяет легко интегрировать с другими хуками, например useReducer или useContext.

Советы по структуре компонентов

  • Разделение логики и визуализации: Компонент с render prop или function as children должен отвечать только за состояние и поведение. UI определяется функцией.
  • Минимизация вложенности: Избыточная вложенность функций может затруднить чтение. Иногда лучше объединять несколько логик в один компонент с несколькими render props.
  • Документирование API: Явно указывать, что функция получает и что должна возвращать. Это критично для переиспользуемых компонентов.

Отличия Render Props и Function as Children

Параметр Render Props Function as Children
Синтаксис Передача функции через проп render Передача функции как дочернего элемента
Читаемость Немного громоздкий Более лаконичный и современный
Совместимость Подходит для классовых и функциональных компонентов Обычно используется с функциональными компонентами
Гибкость Можно использовать несколько render props Обычно одна функция children, что упрощает структуру

Заключение концепции

Render Props и Function as Children обеспечивают мощный инструмент для композиции компонентов в Preact. Они позволяют изолировать логику от представления, улучшить переиспользуемость и создавать гибкие, динамические интерфейсы без дублирования кода. В функциональных компонентах с хуками этот паттерн становится естественным и удобным способом управления состоянием и побочными эффектами.