Render props паттерн

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

Основные концепции

В Stencil компонент описывается с помощью класса и декораторов, таких как @Component, @Prop, @State и @Event. Паттерн render props строится вокруг передачи функции через @Prop, которая принимает текущее состояние компонента и возвращает JSX для рендера.

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

  • @Prop() может быть функцией, которая управляет рендерингом внутреннего состояния.
  • @State() хранит локальное состояние, которое может быть передано в render prop.
  • Такой подход обеспечивает разделение логики и представления, улучшает повторное использование кода и гибкость компонентов.

Пример реализации

import { Component, h, Prop, State } from '@stencil/core';

@Component({
  tag: 'counter-render-prop',
  shadow: true
})
export class CounterRenderProp {
  @State() count: number = 0;

  @Prop() render: (count: number, increment: () => void) => any;

  increment = () => {
    this.count += 1;
  }

  render() {
    return this.render(this.count, this.increment);
  }
}

В этом примере:

  • count хранит текущее значение счетчика.
  • increment — метод увеличения счетчика.
  • render — функция, переданная через @Prop, которая принимает count и increment и возвращает JSX для рендера.

Использование компонента с render props

<counter-render-prop
  render={(count, increment) => (
    <div>
      <p>Текущее значение: {count}</p>
      <button onCl ick={increment}>Увеличить</button>
    </div>
  )}
></counter-render-prop>

Такой подход позволяет полностью контролировать отображение компонента, используя внешнюю логику и стили, не изменяя внутреннюю реализацию.

Комбинация с локальным состоянием и эффектами

Stencil предоставляет хук @Watch для отслеживания изменений @Prop и @State. Render props могут использовать эти изменения для динамического рендеринга.

import { Component, h, Prop, State, Watch } from '@stencil/core';

@Component({
  tag: 'timer-render-prop',
  shadow: true
})
export class TimerRenderProp {
  @State() time: number = 0;
  @Prop() render: (time: number) => any;

  intervalId: any;

  componentDidLoad() {
    this.intervalId = setInterval(() => this.time += 1, 1000);
  }

  disconnectedCallback() {
    clearInterval(this.intervalId);
  }

  render() {
    return this.render(this.time);
  }
}

Этот компонент может быть использован так:

<timer-render-prop
  render={(time) => <p>Прошло секунд: {time}</p>}
></timer-render-prop>

Render props обеспечивают высокую степень гибкости и позволяют:

  • Изменять визуальное представление компонента без модификации логики.
  • Создавать абстрактные компоненты с переиспользуемым поведением.
  • Делать компоненты более предсказуемыми и модульными.

Советы по использованию render props

  • Render prop должна быть чистой функцией, не изменяющей внутреннее состояние компонента напрямую.
  • Передаваемая функция должна быть максимально легкой и не создавать сложной логики внутри JSX.
  • Использовать render props там, где требуется повторное использование логики с различным отображением.
  • В сочетании с @State и @Watch можно создавать динамические и реактивные компоненты, которые реагируют на изменения данных без переписывания JSX внутри самого компонента.

Преимущества паттерна в Stencil

  1. Повторное использование логики: внутренние состояния и методы компонента можно использовать в разных визуальных представлениях.
  2. Разделение логики и представления: логика остается внутри компонента, JSX передается через функцию.
  3. Упрощение тестирования: визуальная часть отделена от логики, что облегчает написание юнит-тестов.
  4. Гибкая кастомизация: внешний код может управлять стилями и структурой без модификации внутреннего компонента.

Render props в Stencil дают возможность создавать мощные, гибкие и переиспользуемые компоненты, сохраняя при этом реактивность и модульность, присущие веб-компонентам.