Skip to main content

Що робить патерн "Function as Child"?

Визначення

Function as Child (FaCC) - це патерн, при якому children компонента - не JSX-елемент, а функція, яку компонент викликає, передаючи їй дані чи логіку.

Тобто компонент сам не рендерить UI, а робить делегування - віддає дані назовні, щоб зовнішній код вирішив, що і як відмалювати.


Приклад: трекінг позиції курсора

Звичайний спосіб

javascript
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

javascript
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.


Що відбувається під капотом

  1. Компонент отримує children
  2. Перевіряє, що це функція
  3. Викликає її з якимись даними
javascript
function Provider({ children }) { const data = { user: "Tim", isLoggedIn: true }; return children(data); // викликаємо функцію }

Таким чином, компонент "ділиться" станом назовні, але не контролює, як він відображається.


Реальний приклад: <DataFetcher>

javascript
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)

javascript
<Mouse>{(pos) => <Cursor pos={pos} />}</Mouse>

Стало (Hook)

javascript
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" - це спосіб зробити компонент джерелом даних, не вирішуючи за розробника, як саме їх відмалювати.

Компонент ділиться логікою → батько керує відображенням.

Коротка відповідь

Для співбесіди
Premium

Коротка відповідь допоможе вам впевнено відповідати на цю тему під час співбесіди.