Render Props и Higher-Order Components

Render Props

Концепция Render Props предполагает передачу функции через пропс, которая отвечает за рендеринг части UI. Этот подход позволяет компонентам делиться логикой состояния или поведения без дублирования кода.

import { Component } from 'inferno';

class MouseTracker extends Component {
  constructor(props) {
    super(props);
    this.state = { x: 0, y: 0 };
    this.handleMouseMove = this.handleMouseMove.bind(this);
  }

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

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

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

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

  • this.props.render — функция, передаваемая извне, которая возвращает JSX.
  • Состояние компонента может быть инкапсулировано и использовано в разных UI через render props.
  • Позволяет создавать переиспользуемые абстракции без ограничения структуры компонентов.

Преимущества Render Props:

  • Гибкость: можно динамически изменять рендеринг без переписывания компонента.
  • Чистая инкапсуляция логики состояния.
  • Легко комбинируется с другими компонентами и библиотеками.

Недостатки:

  • Может привести к “callback hell”, если вложенных render props слишком много.
  • Иногда усложняет структуру дерева компонентов.

Higher-Order Components (HOC)

Higher-Order Component — это функция, которая принимает компонент и возвращает новый компонент с расширенной функциональностью. HOC используется для повторного использования логики между разными компонентами.

import { Component } from 'inferno';

// HOC для отслеживания размера окна
function withWindowSize(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = { width: window.innerWidth, height: window.innerHeight };
      this.handleResize = this.handleResize.bind(this);
    }

    componentDidMount() {
      window.addEventListener('resize', this.handleResize);
    }

    componentWillUnmount() {
      window.removeEventListener('resize', this.handleResize);
    }

    handleResize() {
      this.setState({ width: window.innerWidth, height: window.innerHeight });
    }

    render() {
      return <WrappedComponent {...this.props} windowSize={this.state} />;
    }
  };
}

// Использование
class DisplaySize extends Component {
  render() {
    const { width, height } = this.props.windowSize;
    return <div>Ширина: {width}, Высота: {height}</div>;
  }
}

const EnhancedDisplaySize = withWindowSize(DisplaySize);

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

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

Преимущества HOC:

  • Повторное использование логики без дублирования.
  • Возможность добавлять функциональность к множеству компонентов.
  • Упрощает тестирование исходного компонента.

Недостатки:

  • Может создавать глубокие деревья обёрток, что усложняет отладку.
  • Не всегда удобно комбинировать несколько HOC.

Сравнение Render Props и HOC

Особенность Render Props Higher-Order Component
Передача состояния Через функцию render Через props нового компонента
Чистота исходного компонента Сохраняется Сохраняется
Сложность структуры Может увеличиваться при вложенности Глубокие деревья обёрток
Использование Для динамического рендеринга Для расширения функциональности
Комбинирование Легко комбинируются Требует аккуратного составления

Практические советы

  • Render Props удобны для компонентов с динамическим рендерингом на основе состояния или вычислений.
  • HOC лучше использовать для добавления функциональности, которая не зависит от конкретного UI.
  • Для больших приложений сочетание этих подходов позволяет создавать гибкую архитектуру и уменьшить дублирование кода.
  • В Inferno, как и в React, оба подхода полностью совместимы и интегрируются с виртуальным DOM, обеспечивая высокую производительность.

Комбинирование Render Props и HOC

Можно создавать HOC, которые принимают render props для дополнительного кастомного рендеринга:

function withMouseTracker(WrappedComponent) {
  return class extends Component {
    constructor(props) {
      super(props);
      this.state = { x: 0, y: 0 };
      this.handleMouseMove = this.handleMouseMove.bind(this);
    }

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

    render() {
      return (
        <div onMouseM ove={this.handleMouseMove}>
          <WrappedComponent {...this.props} mouse={this.state} />
        </div>
      );
    }
  };
}

// Использование
const TrackerDisplay = ({ mouse }) => <h2>({mouse.x}, {mouse.y})</h2>;
const EnhancedTracker = withMouseTracker(TrackerDisplay);

Такой подход сочетает мощь HOC для управления состоянием и Render Props (или просто передачу данных через props) для кастомного рендеринга, создавая максимально гибкую архитектуру компонентов.