Що робить патерн "Function as Child"?
Визначення
Function as Child (FaCC) - це патерн, при якому
childrenкомпонента - не JSX-елемент, а функція, яку компонент викликає, передаючи їй дані чи логіку.
Тобто компонент сам не рендерить UI, а робить делегування - віддає дані назовні, щоб зовнішній код вирішив, що і як відмалювати.
Приклад: трекінг позиції курсора
Звичайний спосіб
function MouseTracker() {
const [pos, setPos] = useState({ x: 0, y: 0 });
return (
<div onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}>
<p>Позиція: {pos.x}, {pos.y}</p>
</div>
);
}Працює, але UI і логіка жорстко пов'язані.
Function as Child
function Mouse({ children }) {
const [pos, setPos] = useState({ x: 0, y: 0 });
return (
<div onMouseMove={(e) => setPos({ x: e.clientX, y: e.clientY })}>
{children(pos)} {/* функція-дитина */}
</div>
);
}
// Використовуємо:
function App() {
return (
<Mouse>
{(pos) => (
<p>Позиція курсора: {pos.x}, {pos.y}</p>
)}
</Mouse>
);
}Тут
<Mouse>- компонент з логікою, аAppвирішує, що рендерити, передаючи функцію якchildren.
Що відбувається під капотом
- Компонент отримує
children - Перевіряє, що це функція
- Викликає її з якимись даними
function Provider({ children }) {
const data = { user: "Tim", isLoggedIn: true };
return children(data); // викликаємо функцію
}Таким чином, компонент "ділиться" станом назовні, але не контролює, як він відображається.
Реальний приклад: <DataFetcher>
function DataFetcher({ url, children }) {
const [data, setData] = useState(null);
useEffect(() => {
fetch(url)
.then((r) => r.json())
.then(setData);
}, [url]);
return children(data); // функція отримує дані
}
// Використовуємо
<DataFetcher url="/api/user">
{(data) =>
data ? <p>Привіт, {data.name}!</p> : <p>Завантаження...</p>
}
</DataFetcher>Компонент
DataFetcherвідповідає лише за логіку завантаження А зовнішній код вирішує, як відмалювати дані
Function as Child vs Render Props
Ці два патерни майже ідентичні, різниця - у синтаксисі й у назві пропа, куди передається функція:
| Патерн | Як використовується |
|---|---|
| Render Props | <Component render={(data) => <UI data={data} />} /> |
| Function as Child | <Component>{(data) => <UI data={data} />}</Component> |
Обидва роблять одне й те саме:
- отримують функцію як аргумент,
- викликають її,
- рендерять те, що вона повертає.
У React перевагу віддають
children- це більш природний JSX-синтаксис, ніж окремий пропrender.
Коли застосовувати Function as Child
Добре підходить, коли:
- Потрібно ділитися логікою (scroll, resize, mouse, fetch, auth)
- Компонент - "поведінковий", а не візуальний
- Хочеш інвертувати контроль: компонент надає дані, а батько вирішує, як їх відмалювати
Не варто застосовувати, якщо:
- компонент має бути простим UI-блоком,
- логіка не потребує гнучкості відображення (тоді краще кастомний хук).
Зв'язок із сучасними хуками
Сьогодні замість Function as Child часто використовують кастомні хуки,
які роблять те саме, але без додаткового JSX-рівня:
Було (FaCC)
<Mouse>{(pos) => <Cursor pos={pos} />}</Mouse>Стало (Hook)
function CursorTracker() {
const pos = useMouse();
return <Cursor pos={pos} />;
}Хуки - це еволюція патернів
Render Props/Function as Child: вони вирішують те саме завдання - розділення логіки й представлення, але простіше.
Підсумок
| Що | Опис |
|---|---|
| Ідея | Передавати функцію в children, щоб компонент ділився станом |
| Механізм | Компонент викликає children(data) |
| Мета | Розділити логіку й UI, не нав'язуючи структуру |
| Приклад | <Mouse>{(pos) => <UI pos={pos} />}</Mouse> |
| Сучасна альтернатива | Кастомні хуки |
| Застосовується для | Поведінки (scroll, resize, mouse, fetch, auth) |
Головна думка: "Function as Child" - це спосіб зробити компонент джерелом даних, не вирішуючи за розробника, як саме їх відмалювати.
Компонент ділиться логікою → батько керує відображенням.
Коротка відповідь
Для співбесідиКоротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.