Урок 02

Типизация пропсов

Базовый пример

type ButtonProps = {
    label: string;
    onClick: () => void;
};

function Button({ label, onClick }: ButtonProps) {
    return <button onClick={onClick}>{label}</button>;
}

Inline тип

function Greeting({ name }: { name: string }) {
    return <p>Привет, {name}</p>;
}

Работает, но при росте компонента тип лучше выносить.

interface vs type

// type
type ButtonProps = {
    label: string;
};

// interface
interface ButtonProps {
    label: string;
}

Оба работают. В React чаще используют type — гибче.

Опциональные пропсы

type ButtonProps = {
    label: string;
    variant?: 'primary' | 'secondary';
    disabled?: boolean;
};

Значения по умолчанию

function Button({ variant = 'primary', disabled = false }: ButtonProps) {
    // ...
}

Или через дефолтный параметр:

function Button({ variant = 'primary' }: ButtonProps) {}

Дети (children)

import type { ReactNode } from 'react';

type CardProps = {
    title: string;
    children: ReactNode;
};

function Card({ title, children }: CardProps) {
    return (
        <div>
            <h2>{title}</h2>
            {children}
        </div>
    );
}

ReactNode — самый гибкий тип для детей: строка, JSX, массив, null.

Другие варианты:

  • ReactNode — всё что угодно
  • ReactElement — только JSX-элемент
  • JSX.Element — то же самое
  • string | JSX.Element — строка или элемент

PropsWithChildren

import type { PropsWithChildren } from 'react';

type CardProps = PropsWithChildren<{
    title: string;
}>;

function Card({ title, children }: CardProps) {}

PropsWithChildren<T> добавляет children?: ReactNode.

Функции как пропсы

type ButtonProps = {
    onClick: () => void;
    onDelete: (id: number) => void;
    onSubmit: (data: FormData) => Promise<void>;
};

Массивы и объекты

type ListProps = {
    items: string[];
    config: { sort: 'asc' | 'desc' };
    users: User[];
};

Union пропсов

type Props =
    | { variant: 'text'; children: string }
    | { variant: 'icon'; icon: ReactNode }
    | { variant: 'link'; href: string; children: string };

function Button(props: Props) {
    if (props.variant === 'text') return <button>{props.children}</button>;
    if (props.variant === 'icon') return <button>{props.icon}</button>;
    return <a href={props.href}>{props.children}</a>;
}

Каждый вариант — точный набор пропсов.

Дефолтные значения

Для компонента с optional-пропсами:

function Alert({ type = 'info', message }: AlertProps) {}

Экспорт типа

export type ButtonProps = {
    label: string;
};

export function Button({ label }: ButtonProps) {}

Тип можно импортировать в других файлах.

Итоги

  • type или interface для пропсов
  • children: ReactNode для детей
  • PropsWithChildren<T> сокращает
  • Union для вариантов
  • Экспортируй тип, если нужен снаружи