Синтаксис компонентов
Компоненты — основа для создания пользовательских интерфейсов в React. Синтаксис компонентов — стандартный способ писать их во Flow: специальная примитивная конструкция компонента, включённая по умолчанию и имеющая ряд преимуществ перед обычными функциональными компонентами:
-
Отдельные именованные параметры вместо объекта props. Устраняет дублирование деструктуризации и типизации в
({name}: {name: string})и необходимость оборачивать props вReadonly<{...}>— параметры компонентов по умолчанию доступны только для чтения. -
Без аннотации возвращаемого типа. Flow выводит и проверяет
React.Nodeи отклоняет компоненты, которые неявно возвращают значение в какой-либо ветви. -
Необязательная секция
renders, ограничивающая форму JSX, которую может создавать компонент, и позволяющая задавать контракты композиции для компонентов-обёрток и HOC. -
Структурные правила, проверяемые при разборе/проверке типов: запрет на
thisи вложенные определения компонентов; компоненты можно отображать только как JSX — их нельзя вызывать как обычные функции. См. раздел Правила для компонентов ниже. - Улучшенная поддержка ссылок React благодаря специальной позиции параметра ref.
Синтаксис component доступен только во Flow. TypeScript моделирует компоненты как обычные функции с типом props. Проверка типов Flow обеспечивает соблюдение правил, которые при использовании модели обычных функций в TypeScript остаются на усмотрение разработчика или правил ESLint. Полное сравнение см. в разделе Синтаксис компонентов во Flow.
Основное использование
Компонент можно объявить с помощью синтаксиса компонентов примерно так же, как объявляют функцию:
import * as React from 'react';
component Introduction(name: string, age: number) {
return <h1>My name is {name} and I am {age} years old</h1>
} Компонент можно использовать непосредственно в JSX: <Introduction age={9} name="Mr. Flow" />.
Обратите внимание на несколько важных деталей:
- имена параметров props, объявленные в компоненте Introduction, совпадают с именами props, переданными компоненту Introduction в JSX
- порядок параметров в объявлении не обязательно должен совпадать с порядком их передачи в JSX
Параметры
Дочерние элементы
children — самый распространённый prop, а в синтаксисе компонентов это просто обычный параметр; объявите его так же, как любой другой. Обычно его тип — React.Node, который охватывает всё, что можно отобразить (элементы, строки, массивы, null и многое другое):
import * as React from 'react';
component Card(title: string, children: React.Node) {
return (
<section>
<h2>{title}</h2>
{children}
</section>
);
}
<Card title="Welcome">
<p>Hello!</p>
</Card>; Не нужны ни вспомогательная конструкция React.PropsWithChildren, ни добавление children к типу объекта props. children — это просто параметр, и ему следует присвоить тип React.Node.
Иногда дочерние элементы должны быть более конкретными, чем «всё, что можно отобразить». Чтобы ограничить передаваемые элементы, укажите для children тип рендеринга вместо React.Node. Например, компонент Menu, дочерними элементами которого должны быть MenuItem, может требовать их с помощью renders*:
import * as React from 'react';
component MenuItem(label: string) {
return <li>{label}</li>;
}
component Menu(children: renders* MenuItem) {
return <ul>{children}</ul>;
}
<Menu>
<MenuItem label="Home" />
<MenuItem label="Profile" />
</Menu>; Типы рендеринга охватывают полный набор ограничений: renders для одного обязательного элемента, renders? для необязательного и renders* для любого количества дочерних элементов.
Строковые параметры/переименование параметров
Компоненты также позволяют переименовывать параметры. Это полезно, если имя параметра не является допустимым идентификатором JavaScript:
import * as React from 'react';
component RenamedParameter(
'required-renamed' as foo: number,
'optional-renamed' as bar?: number,
'optional-with-default-renamed' as baz?: number = 3,
) {
foo as number; // OK
bar as number | void; // OK
baz as number; // OK
return <div />;
} Остаточные параметры
Иногда нет необходимости явно перечислять каждый prop, поскольку вы не собираетесь обращаться к ним по отдельности в компоненте. Это часто бывает при написании компонента-обёртки, которому нужно передать props из вашего компонента во вложенный:
import * as React from 'react';
// star.js
component Star(color: string, size: number) {
return <div />;
}
type StarProps = React.PropsOf<Star>;
// blue_star.js
component BlueStar(...props: StarProps) {
return <Star {...props} color="blue" />;
} В аннотации остаточных параметров используется тип объекта, поэтому для описания более сложных шаблонов props можно применять существующие средства работы с типами, например объединение объектов и Pick:
import * as React from 'react';
component OtherComponent(foo: string, bar: number) {
return <div>{foo} {bar}</div>;
}
component FancyProps(
...props: {
...React.PropsOf<OtherComponent>,
additionalProp: string,
}
) {
return <OtherComponent foo={props.foo} bar={props.bar} />;
} Props с непересекающимся объединением
С помощью остаточного параметра с типом непересекающегося объединения можно определить компонент, принимающий один из нескольких вариантов формы props. Уточняйте тип props внутри тела компонента, используя поле-дискриминант:
import * as React from 'react';
type TextProps = {kind: 'text', text: string};
type ImageProps = {kind: 'image', src: string, alt: string};
type Props = TextProps | ImageProps;
component MediaItem(...props: Props) {
if (props.kind === 'text') {
return <span>{props.text}</span>;
} else {
return <img src={props.src} alt={props.alt} />;
}
} Необязательные параметры и значения по умолчанию
В компонентах можно объявлять необязательные параметры и указывать значения по умолчанию:
import * as React from 'react';
component OptionalAndDefaults(
color: string = "blue",
extraMessage?: string,
) {
let message = `My favorite color is ${color}.`;
if (extraMessage != null) {
message += `\n${extraMessage}`;
}
return <p>{message}</p>
}
<OptionalAndDefaults /> // No error, all of the parameters are optional! Деструктуризация параметров
Оператор as также позволяет деструктурировать параметры:
import * as React from 'react';
component Destructuring(
config as {color, height}: Readonly<{color: number, height: number}>,
) { return <div /> } Остаточные параметры можно деструктурировать без использования as:
import * as React from 'react';
type Props = Readonly<{ color: string, height: number }>;
component DestructuredRest(...{color, height}: Props) { return <div /> } Параметры ref
Чтобы обращаться к ссылкам в компонентах, достаточно добавить параметр ref.
import * as React from 'react';
component ComponentWithARef(ref: React.RefSetter<HTMLElement>) {
return <div ref={ref} />;
} За кулисами синтаксис компонентов генерирует подходящий код для целевой версии React: при использовании React 18 компонент оборачивается в необходимый вызов React.forwardRef, а при использовании React 19 параметр ref передаётся как обычный prop (в React 19 forwardRef больше не нужен). Параметр ref объявляется одинаково независимо от целевой версии. Единственное ограничение для refs: их нужно объявлять как встроенный параметр; refs внутри остаточных параметров не поддерживаются, поскольку синтаксису компонентов необходимо статически определять ref по объявлению компонента.
Подробнее о типизации refs см. в разделе Refs, в том числе о хуке useRef, React.RefObject, React.RefSetter, callback refs и чтении ref.current.
Правила для компонентов
Синтаксис компонентов накладывает несколько ограничений, помогающих обеспечить корректность:
-
Возвращаемые значения должны быть подтипом
React.Node, иначе React может аварийно завершить работу при отображении компонента. -
Все ветви компонента должны завершаться явным оператором return. Хотя
undefinedявляется допустимым возвращаемым значением, мы не раз видели, как явный оператор return помог бы избежать ошибок в рабочей среде. -
В компоненте нельзя использовать
this. -
Нельзя определять компонент внутри другого компонента или хука. Это вызывает предупреждение линтера
nested-component, поскольку вложенный компонент получает новую идентичность при каждом рендеринге, из-за чего React каждый раз размонтирует и монтирует его заново. Если внутренняя «компонента» не использует хуки, вместо неё используйте обычную функцию или вынесите компонент на верхний уровень. -
Компоненты нельзя вызывать как обычные функции — их можно отображать только как JSX. Прямой вызов
Foo(props)приводит к ошибке[react-rule-call-component]. Вместо этого используйте<Foo {...props} />.
Поэтому следующие компоненты недопустимы:
import * as React from 'react';
component InvalidReturnValue() {
return new Object(); // ERROR: Value does not match `React.Node` type
}
component ImplicitReturn(someCond: boolean) { // ERROR
if (someCond) {
return <h1>Hello World!</h1>;
}
// ERROR: No return in this branch
}
component UsesThis() {
this.foo = 3; // ERROR: Accessing `this`
return null;
}
component Greeting() {
return <h1>Hello</h1>;
}
const elem = Greeting({}); // ERROR [react-rule-call-component]: use <Greeting /> instead
import typeof с обобщёнными компонентами
Если применить import typeof к экспорту по умолчанию обобщённого компонента, импортированный псевдоним типа представляет обобщённый параметр как обязательный аргумент типа. Это значит, что использовать импортированный тип без явного указания аргумента нельзя:
// GenericComp.js
import * as React from 'react';
component MyComp<T>(data: T, render: (item: T) => React.Node) {
return render(data);
}
export default MyComp;
// Consumer.js
import typeof MyComp from './GenericComp';
type CompType = MyComp; // Error: missing 1 type argument Чтобы обойти ошибку преждевременной инстанциации при объявлении, используйте пространство имён import typeof и обращайтесь к его элементу по индексу. Учтите, что при этом полиморфизм теряется — результирующий тип рассматривается как конкретный (неполиморфный) тип компонента, поэтому инстанцировать его с аргументом типа, например CompType<number>, по-прежнему нельзя:
import typeof * as GenericCompModule from './GenericComp'; type CompType = GenericCompModule['default']; // Declaration works, but CompType is no longer polymorphic
Доступность
Синтаксис компонентов включён по умолчанию начиная с Flow v0.317. В более старых версиях добавьте component_syntax=true в раздел [options] файла .flowconfig.
См. также
- Обобщённые типы — использование параметров обобщённых типов в props компонентов
- Вариантность — понимание доступных только для чтения props и ковариантности
© 2013–present Facebook Inc.
Licensed under the MIT License.
https://flow.org/en/docs/react/component-syntax