Пошук уроків, статей та іншого контенту
Навчитеся типізувати функціональні React-компоненти, їхні параметри та тип значення, яке вони повертають.
Функціональний компонент — це JavaScript-функція, яка повертає JSX:
function Welcome() {
return <h1>Вітаємо!</h1>;
}параметри функції, зазвичай props;
значення, яке повертає компонент.
Найчастіше тип пропсів описують окремим типом або інтерфейсом.
Розглянемо компонент, який отримує ім’я користувача та його вік:
type UserCardProps = {
name: string;
age: number;
};
function UserCard({ name, age }: UserCardProps) {
return (
<article>
<h2>{name}</h2>
<p>Вік: {age}</p>
</article>
);
}UserCardProps описує форму об’єкта props:
name має бути рядком;
age має бути числом.
Під час використання компонента TypeScript перевірить ці значення:
function App() {
return (
<main>
<UserCard name="Олена" age={28} />
<UserCard name="Андрій" age={35} />
</main>
);
}У цьому прикладі:
значення атрибута name передається як рядок;
значення атрибута age передається як число.
Такий код TypeScript вважатиме помилковим:
<UserCard name="Олена" age="28" />age очікує число, але отримує рядок.
За замовчуванням усі властивості типу є обов’язковими:
type ButtonProps = {
text: string;
disabled: boolean;
};
function Button({ text, disabled }: ButtonProps) {
return (
<button disabled={disabled}>
{text}
</button>
);
}Для створення необов’язкового пропса після його назви ставлять знак ?:
type ButtonProps = {
text: string;
disabled?: boolean;
};
function Button({ text, disabled = false }: ButtonProps) {
return (
<button disabled={disabled}>
{text}
</button>
);
}Тепер disabled можна не передавати:
function App() {
return <Button text="Зберегти" />;
}Якщо пропс не передано, його значення буде undefined. У прикладі вище деструктуризація встановлює значення false за замовчуванням.
Для пропсів використовують стандартні типи TypeScript:
type ProductProps = {
title: string;
price: number;
isAvailable: boolean;
};
function Product({ title, price, isAvailable }: ProductProps) {
return (
<section>
<h2>{title}</h2>
<p>Ціна: {price} грн</p>
<p>{isAvailable ? "Є в наявності" : "Немає в наявності"}</p>
</section>
);
}Використання:
<Product
title="Бездротова клавіатура"
price={1800}
isAvailable={true}
/>Логічні значення в JSX потрібно передавати у фігурних дужках:
<Product
title="Миша"
price={900}
isAvailable={false}
/>Запис isAvailable="false" передасть рядок "false", а не логічне значення false.
interfaceДля опису пропсів можна використовувати interface:
interface UserProps {
name: string;
email: string;
}
function User({ name, email }: UserProps) {
return (
<div>
<strong>{name}</strong>
<span>{email}</span>
</div>
);
}Для простих пропсів type та interface часто взаємозамінні. Важливо не те, який саме синтаксис використано, а те, що тип пропсів чітко описаний.
TypeScript часто може сам визначити тип значення, яке повертає функція:
type GreetingProps = {
name: string;
};
function Greeting({ name }: GreetingProps) {
return <h1>Привіт, {name}!</h1>;
}У цьому випадку TypeScript бачить, що компонент повертає JSX, тому окремо вказувати тип повернення не обов’язково.
За потреби тип повернення можна вказати явно. Для React-компонента, який повертає JSX або null, зручно використовувати React.JSX.Element | null:
import type { React } from "react";
type MessageProps = {
text: string;
visible: boolean;
};
function Message({
text,
visible,
}: MessageProps): React.JSX.Element | null {
if (!visible) {
return null;
}
return <p>{text}</p>;
}Компонент може повернути null, коли нічого не потрібно відображати. Саме тому тип повернення містить | null.
У проєктах із новішими типами React можна також записати:
type MessageProps = {
text: string;
visible: boolean;
};
function Message({
text,
visible,
}: MessageProps): JSX.Element | null {
if (!visible) {
return null;
}
return <p>{text}</p>;
}Конкретний запис залежить від налаштувань і версії типів React у проєкті. Якщо TypeScript може вивести тип повернення самостійно, явна анотація не потрібна.
childrenchildren — це вміст, переданий між відкривальним і закривальним тегами компонента:
function App() {
return (
<Card>
<p>Вміст картки</p>
</Card>
);
}Для children зазвичай використовують тип React.ReactNode. Він охоплює JSX-елементи, рядки, числа, масиви елементів, null та інші значення, які React може відобразити.
import type { ReactNode } from "react";
type CardProps = {
title: string;
children: ReactNode;
};
function Card({ title, children }: CardProps) {
return (
<section>
<h2>{title}</h2>
<div>{children}</div>
</section>
);
}Використання:
function App() {
return (
<Card title="Профіль">
<p>Інформація про користувача</p>
</Card>
);
}Якщо children не є обов’язковим, додайте ?:
import type { ReactNode } from "react";
type PanelProps = {
children?: ReactNode;
};
function Panel({ children }: PanelProps) {
return <div>{children}</div>;
}Компонент може отримувати функцію як пропс. Тип функції описують через її параметри та тип повернення:
type SaveButtonProps = {
onSave: () => void;
};
function SaveButton({ onSave }: SaveButtonProps) {
return (
<button type="button" onClick={onSave}>
Зберегти
</button>
);
}onSave: () => void означає, що:
функція не приймає параметрів;
функція нічого не повертає.
Приклад використання:
function App() {
function handleSave() {
console.log("Дані збережено");
}
return <SaveButton onSave={handleSave} />;
}Якщо функція повинна отримувати параметр, це також потрібно вказати:
type UserActionsProps = {
onSelect: (userId: number) => void;
};
function UserActions({ onSelect }: UserActionsProps) {
return (
<button type="button" onClick={() => onSelect(42)}>
Обрати користувача
</button>
);
}Тут onSelect приймає число та нічого не повертає.
import type { ReactNode } from "react";
type LayoutProps = {
title: string;
children: ReactNode;
showFooter?: boolean;
};
type ActionButtonProps = {
text: string;
onClick: () => void;
};
function ActionButton({
text,
onClick,
}: ActionButtonProps): React.JSX.Element {
return (
<button type="button" onClick={onClick}>
{text}
</button>
);
}
function Layout({
title,
children,
showFooter = true,
}: LayoutProps): React.JSX.Element {
return (
<div>
<header>
<h1>{title}</h1>
</header>
<main>{children}</main>
{showFooter && <footer>Кінець сторінки</footer>}
</div>
);
}
function App(): React.JSX.Element {
function handleButtonClick(): void {
console.log("Кнопку натиснуто");
}
return (
<Layout title="Панель керування">
<p>Вітаємо в застосунку!</p>
<ActionButton
text="Виконати дію"
onClick={handleButtonClick}
/>
</Layout>
);
}
export default App;У прикладі типізовано:
title як string;
children як ReactNode;
showFooter як необов’язковий boolean;
onClick як функцію без параметрів і результату;
типи повернення компонентів;
тип повернення обробника події.
React.FCКомпоненти можна записувати за допомогою React.FC:
import type { FC } from "react";
type TitleProps = {
text: string;
};
const Title: FC<TitleProps> = ({ text }) => {
return <h1>{text}</h1>;
};Однак для початку простіше й прозоріше типізувати параметри безпосередньо:
type TitleProps = {
text: string;
};
function Title({ text }: TitleProps) {
return <h1>{text}</h1>;
}Такий запис явно показує:
який тип мають пропси;
які параметри отримує функція;
що функція є звичайним компонентом.
У сучасному TypeScript-коді React.FC не є обов’язковим. Обидва підходи працюють, але важливо дотримуватися одного стилю в межах проєкту.
type ProfileProps = {
age: number;
};
function Profile({ age }: ProfileProps) {
return <p>Вік: {age}</p>;
}
// Помилка: age має бути number
<Profile age="25" />;Правильний варіант:
<Profile age={25} />Якщо пропс іноді може бути відсутнім, це потрібно відобразити в типі:
type GreetingProps = {
name?: string;
};
function Greeting({ name = "гість" }: GreetingProps) {
return <p>Привіт, {name}!</p>;
}Без ? TypeScript вимагатиме передавати name щоразу.
type ModalProps = {
open: boolean;
};
function Modal({ open }: ModalProps) {
return open ? <div>Модальне вікно</div> : null;
}
// Помилка: "true" є рядком
<Modal open="true" />;Потрібно використовувати фігурні дужки:
<Modal open={true} />Неправильно:
<ActionButton onClick={handleButtonClick()} />Тут функція викликається під час рендерингу.
Правильно:
<ActionButton onClick={handleButtonClick} />У такому випадку компонент отримує саму функцію й викличе її пізніше.
childrenЯкщо компонент має приймати різний JSX-вміст, не варто типізувати children лише як JSX.Element без потреби. Для звичайного вмісту React використовуйте ReactNode:
import type { ReactNode } from "react";
type BoxProps = {
children: ReactNode;
};Пропси функціонального компонента типізують через type або interface.
Тип пропсів передають у параметри функції під час деструктуризації.
Необов’язкові властивості позначають знаком ?.
children зазвичай має тип ReactNode.
Функції в пропсах описують через параметри та тип повернення, наприклад () => void.
TypeScript часто сам визначає тип, який повертає компонент.
Якщо компонент може повертати null, тип повернення може бути React.JSX.Element | null.
React.FC не є обов’язковим для типізації функціональних компонентів.