Пошук уроків, статей та іншого контенту
Дізнаєтеся, як типізувати текст, елементи React і функції як значення children.
childrenchildren — це спеціальна властивість компонента, яка містить усе, що передали між відкривальним і закривальним тегами компонента.
<Card>
<h2>Заголовок</h2>
<p>Текст картки</p>
</Card>У цьому прикладі children компонента Card містить елементи <h2> і <p>.
У TypeScript тип children потрібно вказати так само, як і будь-яку іншу властивість компонента.
ReactNodeНайпоширеніший тип для children — ReactNode.
Він підходить для значень, які React може відобразити:
тексту;
чисел;
React-елементів;
масивів елементів;
null;
undefined;
логічних значень, які React не відображає.
import type { ReactNode } from "react";
type CardProps = {
children: ReactNode;
};
function Card({ children }: CardProps) {
return <section className="card">{children}</section>;
}
export default function App() {
return (
<Card>
<h2>Профіль користувача</h2>
<p>Додаткова інформація про користувача.</p>
</Card>
);
}ReactNode — хороший вибір, коли компонент має підтримувати різний вміст:
<Card>Звичайний текст</Card>
<Card>
<strong>Виділений текст</strong>
</Card>
<Card>{42}</Card>
<Card>{null}</Card>childrenЯкщо компонент має приймати лише текст, використовуйте тип string.
type TitleProps = {
children: string;
};
function Title({ children }: TitleProps) {
return <h1>{children}</h1>;
}
export default function App() {
return <Title>Сторінка налаштувань</Title>;
}Тепер TypeScript не дозволить передати в Title React-елемент:
function App() {
return (
<Title>
<strong>Налаштування</strong>
</Title>
);
}Такий код спричинить помилку типізації, тому що children очікує лише string.
Тип string корисний, коли компонент має приймати тільки простий текст:
type ButtonLabelProps = {
children: string;
};
function ButtonLabel({ children }: ButtonLabelProps) {
return <span>{children}</span>;
}Однак у більшості універсальних компонентів для вмісту використовують ReactNode.
Іноді компонент має приймати саме один React-елемент, а не будь-який React-вміст.
Для цього можна використати ReactElement.
import type { ReactElement } from "react";
type PanelProps = {
children: ReactElement;
};
function Panel({ children }: PanelProps) {
return <div className="panel">{children}</div>;
}
export default function App() {
return (
<Panel>
<p>Цей елемент передано як children.</p>
</Panel>
);
}ReactElement підходить, коли children повинен бути елементом на кшталт:
<p>Текст</p>Але він не підходить для звичайного тексту:
function App() {
return <Panel>Звичайний текст</Panel>;
}У цьому випадку виникне помилка, оскільки рядок не є ReactElement.
Також ReactElement забороняє передавати null:
function App() {
return <Panel>{null}</Panel>;
}Якщо компонент має приймати і React-елемент, і текст, і null, використовуйте ReactNode.
ReactNode та ReactElementЦі типи мають різне призначення:
import type { ReactElement, ReactNode } from "react";
type AnyContentProps = {
children: ReactNode;
};
type ElementOnlyProps = {
children: ReactElement;
};ReactNode — ширший тип. Він охоплює всі значення, які React може відобразити.
ReactElement — конкретний React-елемент, створений JSX або викликом createElement.
Практичне правило:
використовуйте ReactNode для звичайного вмісту компонента;
використовуйте ReactElement, коли потрібен саме елемент;
використовуйте string, коли дозволений лише текст.
childrenchildren може бути не вмістом для відображення, а функцією. Такий підхід називають функцією-дітьми або render prop.
Функція отримує дані від батьківського компонента та повертає React-вміст.
import type { ReactNode } from "react";
type User = {
name: string;
role: string;
};
type UserProviderProps = {
children: (user: User) => ReactNode;
};
function UserProvider({ children }: UserProviderProps) {
const user: User = {
name: "Олена",
role: "Адміністратор",
};
return <div>{children(user)}</div>;
}
export default function App() {
return (
<UserProvider>
{(user) => (
<p>
{user.name} — {user.role}
</p>
)}
</UserProvider>
);
}Тип властивості:
children: (user: User) => ReactNode;означає:
children повинен бути функцією;
функція отримує об'єкт типу User;
функція повертає ReactNode.
Функцію можна записати окремо:
function renderUser(user: User): ReactNode {
return <p>{user.name}</p>;
}
export default function App() {
return <UserProvider>{renderUser}</UserProvider>;
}Або передати як стрілкову функцію:
<UserProvider>
{(user) => <p>{user.name}</p>}
</UserProvider>TypeScript автоматично знає тип параметра user, тому зазвичай його не потрібно вказувати вручну.
children, яка повертає елементЯкщо функція повинна повертати саме один React-елемент, можна використати ReactElement:
import type { ReactElement } from "react";
type CounterProps = {
children: (count: number) => ReactElement;
};
function Counter({ children }: CounterProps) {
const count = 3;
return <div>{children(count)}</div>;
}
export default function App() {
return (
<Counter>
{(count) => <p>Поточне значення: {count}</p>}
</Counter>
);
}Універсальніше використовувати ReactNode, оскільки функція може повернути текст, фрагмент або null:
type CounterProps = {
children: (count: number) => ReactNode;
};childrenЯкщо компонент може працювати без children, додайте знак питання:
import type { ReactNode } from "react";
type PanelProps = {
children?: ReactNode;
};
function Panel({ children }: PanelProps) {
return <section>{children}</section>;
}
export default function App() {
return <Panel />;
}У цьому випадку можливі обидва варіанти:
<Panel>
<p>Вміст панелі</p>
</Panel>
<Panel />Якщо children є обов'язковим, знак питання не потрібен:
type PanelProps = {
children: ReactNode;
};Тоді компонент потрібно використовувати з вмістом:
<Panel>
<p>Вміст панелі</p>
</Panel>У наступному прикладі показані три варіанти типізації:
import type { ReactElement, ReactNode } from "react";
type BoxProps = {
children: ReactNode;
};
function Box({ children }: BoxProps) {
return <div className="box">{children}</div>;
}
type HeadingProps = {
children: string;
};
function Heading({ children }: HeadingProps) {
return <h1>{children}</h1>;
}
type MessageProps = {
children: (message: string) => ReactElement;
};
function Message({ children }: MessageProps) {
const text = "Повідомлення отримано";
return <div>{children(text)}</div>;
}
export default function App() {
return (
<main>
<Box>
<Heading>Типізація children</Heading>
<p>Box приймає будь-який React-вміст.</p>
</Box>
<Message>
{(message) => <strong>{message}</strong>}
</Message>
</main>
);
}Тут:
Box приймає ReactNode;
Heading приймає лише string;
Message приймає функцію, яка отримує рядок і повертає ReactElement.
JSX.Element для будь-якого childrentype Props = {
children: JSX.Element;
};Такий тип приймає лише один JSX-елемент. Він не дозволяє передати звичайний текст, число або null.
Для звичайного вмісту краще написати:
import type { ReactNode } from "react";
type Props = {
children: ReactNode;
};string, коли потрібна розміткаtype Props = {
children: string;
};Такий тип не дозволяє передати:
<em>Важливий текст</em>Якщо children може містити HTML-подібну React-розмітку, використовуйте ReactNode.
children, коли це не функціяЯкщо тип описаний так:
type Props = {
children: ReactNode;
};children не можна викликати:
children();ReactNode — це вміст, а не функція.
Для функції потрібно явно описати сигнатуру:
type Props = {
children: (value: number) => ReactNode;
};Після цього її можна викликати:
children(10);Не варто залишати функцію без опису очікуваного результату:
type Props = {
children: (value: number) => unknown;
};Для функції, яка повертає вміст React, використовуйте:
import type { ReactNode } from "react";
type Props = {
children: (value: number) => ReactNode;
};children — спеціальна властивість для вмісту між тегами компонента.
ReactNode підходить для більшості звичайних компонентів.
string використовується, коли дозволений лише текст.
ReactElement використовується, коли потрібен саме один React-елемент.
Функцію як children описують як сигнатуру функції, наприклад:
children: (value: number) => ReactNode;Знак ? робить children необов'язковим:
children?: ReactNode;