Render Props — это шаблон проектирования компонентов, при котором компонент управляет логикой и состоянием, а визуальное представление передаётся через функцию. В контексте Atomico этот подход позволяет создавать гибкие, переиспользуемые компоненты без жёсткой привязки к конкретной разметке.
В Atomico компонент — это функция, которая возвращает JSX через
html. Для реализации render props создаётся компонент,
принимающий свойство-функцию (обычно называют
children или render) и вызывающий её внутри
своей разметки.
import { c, html, useState } from "atomico";
const Counter = c(
({ render }) => {
const [count, setCount] = useState(0);
return html`
<div>
${render({ count, increment: () => setCount(count + 1) })}
</div>
`;
}
);
export default Counter;
В этом примере компонент Counter хранит состояние
счётчика, а вывод реализуется внешней функцией render. Это
позволяет полностью контролировать отображение компонента извне.
Вызов компонента с render props выглядит следующим образом:
import Counter from "./Counter";
const App = () => html`
<${Counter}
.render=${({ count, increment }) => html`
<p>Счётчик: ${count}</p>
<button @click=${increment}>Увеличить</button>
`}
/>
`;
Здесь логика обновления состояния отделена от визуального представления. Можно создавать несколько разных интерфейсов для одного и того же состояния:
<Counter
.render=${({ count, increment }) => html`
<div>
<strong>${count}</strong>
<button @click=${increment}>+1</button>
</div>
`}
/>
Гибкость интерфейса Render props позволяют менять разметку без модификации логики компонента. Один и тот же компонент может отображаться по-разному в разных частях приложения.
Изоляция состояния Компонент контролирует внутреннее состояние и предоставляет его через функцию, снижая риск прямого вмешательства извне.
Переиспользуемость Логику можно использовать с любым шаблоном разметки, что особенно удобно для библиотек UI-компонентов.
Atomico активно использует хуки (useState,
useEffect, useRef) для управления состоянием.
Render props отлично интегрируются с этой моделью:
const Toggle = c(
({ render }) => {
const [on, setOn] = useState(false);
const toggle = () => setOn(!on);
return html`${render({ on, toggle })}`;
}
);
<Toggle
.render=${({ on, toggle }) => html`
<button @click=${toggle}>
${on ? "Включено" : "Выключено"}
</button>
`}
/>
Здесь Toggle предоставляет логику переключателя, а
отображение кнопки полностью настраивается снаружи.
Render props в Atomico легко комбинируются с HTML-разметкой и компонентами:
<Counter
.render=${({ count, increment }) => html`
<div class="counter-container">
<span>Значение: ${count}</span>
<my-button @click=${increment}>Добавить</my-button>
</div>
`}
/>
Можно использовать как встроенные элементы, так и кастомные веб-компоненты. Важно передавать функцию через свойство, а не как обычный дочерний элемент, чтобы Atomico корректно распознал её как render prop.
Компонент может предоставлять несколько точек для кастомизации через несколько функций:
const List = c(
({ items, renderItem, renderEmpty }) => html`
<ul>
${items.length
? items.map(item => html`<li>${renderItem(item)}</li>`)
: renderEmpty()}
</ul>
`
);
<List
.items=${[]}
.renderItem=${item => html`<strong>${item}</strong>`}
.renderEmpty=${() => html`<em>Список пуст</em>`}
/>
Такой подход повышает универсальность компонента, позволяя задавать поведение для разных ситуаций.
useCallback.Render props в Atomico — мощный инструмент для разделения логики и представления. Они обеспечивают гибкость, переиспользуемость и контроль состояния компонентов, оставаясь совместимыми с хуками и JSX-подобным синтаксисом Atomico. Правильное применение render props помогает строить чистую архитектуру компонентов и упрощает поддержку больших приложений.