コンポーネント構文
コンポーネントは、ReactでUIを構築するための基盤です。コンポーネントは通常JavaScript関数を使用して表現されますが、コンポーネント構文は、関数コンポーネントよりもいくつかの利点を持つコンポーネントプリミティブ値を提供します。たとえば、
- 関数よりも冗長性とボイラープレートが大幅に少ない、より洗練された構文
- Reactの記述に特化して調整された型システムのサポート
- React refのより良いサポート
基本的な使い方
関数を宣言する方法と同様に、コンポーネント構文を使用してコンポーネントを宣言できます
1import * as React from 'react';2
3component Introduction(name: string, age: number) {4 return <h1>My name is {name} and I am {age} years old</h1>5}
JSXでコンポーネントを直接使用できます:<Introduction age={9} name="Mr. Flow" />
。
ここで注意すべき重要な点がいくつかあります
- Introductionコンポーネントで宣言されているpropパラメータ名は、JSXでIntroductionに渡されるprop名と同じです
- 宣言のパラメータの順序は、JSXで指定されている順序と一致する必要はありません
パラメータ
文字列パラメータ/パラメータの名前変更
コンポーネントではパラメータの名前を変更することもできます。これは、パラメータ名が有効なJavaScript識別子でない場合に役立ちます
1import * as React from 'react';2
3component RenamedParameter(4 'required-renamed' as foo: number,5 'optional-renamed' as bar?: number,6 'optional-with-default-renamed' as baz?: number = 3,7) {8 (foo: number); // OK9 (bar: number | void); // OK10 (baz: number); // OK11
12 return <div />;13}
レストパラメータ
コンポーネント内で個別に参照する予定がないため、すべてのpropを明示的にリストしたくない場合があります。これは、別のコンポーネントをラップするコンポーネントを記述し、コンポーネントから内部コンポーネントにpropを渡す必要がある場合に一般的です
import * as React from 'react';
import type {Props as StarProps} from './Star';
import Star from './Star';
component BlueStar(...props: StarProps) {
return <Star {...props} color="blue" />;
}
レストパラメータは、オブジェクト型を注釈として使用します。つまり、オブジェクトスプレッドやPickなどの既存の型ユーティリティを使用して、より複雑なpropパターンに注釈を付けることができます
1import * as React from 'react';2
3component OtherComponent(foo: string, bar: number) {4 return foo + bar;5}6
7component FancyProps(8 ...props: {9 ...React.PropsOf<OtherComponent>,10 additionalProp: string,11 }12) {13 return <OtherComponent foo={props.foo} bar={props.bar} />;14}
オプションパラメータとデフォルト
コンポーネントでは、オプションパラメータを宣言し、デフォルトを指定できます
1import * as React from 'react';2
3component OptionalAndDefaults(4 color: string = "blue",5 extraMessage?: string,6) {7 let message = `My favorite color is ${color}.`;8 if (extraMessage != null) {9 message += `\n${extraMessage}`;10 }11 return <p>{message}</p>12}13
14<OptionalAndDefaults /> // No error, all of the parameters are optional!
パラメータの分割代入
as
演算子を使用すると、パラメータを分割代入することもできます
1import * as React from 'react';2
3component Destructuring(4 config as {color, height}: $ReadOnly<{color: number, height: number}>,5) { return <div /> }
レストパラメータは、asを使用せずに分割代入できます
1import * as React from 'react';2
3type Props = $ReadOnly<{ color: string, height: number }>;4
5component DestructuredRest(...{color, height}: Props) { return <div /> }
Refパラメータ
コンポーネントでrefにアクセスするには、refパラメータを追加するだけです。
1import * as React from 'react';2
3component ComponentWithARef(ref: React.RefSetter<HTMLElement>) {4 return <div ref={ref} />;5}
コンポーネント構文は、コンポーネントが実行時に期待どおりに動作するように、必要なReact.forwardRef呼び出しでコンポーネントをラップします。 refの制限事項の1つは、インラインパラメータとして定義する必要があることです。レストパラメータ内のrefはサポートされていません。これは、forwardRef
呼び出しをコンパイルする必要があるためです。これが正しく機能するには、コンポーネント定義からrefを静的に決定できる必要があります。
コンポーネントの要素
コンポーネント構文で宣言されたコンポーネントの場合、ComponentName
を使用して、そのコンポーネントの要素の型を参照できます。コンポーネント構文を使用していないコンポーネントの場合、React.Element<typeof ComponentName>
と記述する必要があります。
1import * as React from 'react';2
3declare component Foo();4declare component Bar();5
6const foo: Foo = <Foo />;7const bar: Bar = <Foo />; // ERROR
7:19-7:21: Cannot assign `<Foo />` to `bar` because component Foo [1] is incompatible with component Bar [2] in type argument `ElementType` [3]. [incompatible-type-arg]
この構文は、ジェネリックコンポーネントでも機能します
1import * as React from 'react';2
3declare component Foo<T>(prop: T);4
5const foo1: Foo<string> = <Foo prop="" />;6const foo2: Foo<number> = <Foo prop="" />; // Invalid generic type argument 7const foo3: Foo = <Foo prop="" />; // Missing generic type argument
6:27-6:41: Cannot assign `<Foo />` to `foo2` because string [1] is incompatible with number [2] in property `prop` of type argument `P` [3]. [incompatible-type-arg]7:13-7:15: Cannot use component Foo [1] without 1 type argument. [missing-type-arg]
非常に特定の型の要素を要求することはお勧めしません。親コンポーネントと子コンポーネントの結合が強くなります。代わりに、この機能はレンダータイプを簡単に表現できるように設計されています。
コンポーネントのルール
コンポーネント構文は、正確性を確保するために、コンポーネントにいくつかの制限を課します
- 戻り値は
React.Node
のサブタイプである必要があります。そうでない場合、Reactはコンポーネントのレンダリング中にクラッシュする可能性があります。 - コンポーネントのすべてのブランチは、明示的なreturnで終了する必要があります。
undefined
は有効な戻り値ですが、明示的なreturnが本番環境でのバグを防ぐことができた多くのインスタンスを見てきました。 - コンポーネントで
this
を使用することはできません。
したがって、これらのコンポーネントは無効です
1import * as React from 'react';2
3component InvalidReturnValue() {4 return new Object(); // ERROR: Value does not match `React.Node` type 5}6
7component ImplicitReturn(someCond: boolean) { 8 if (someCond) {9 return <h1>Hello World!</h1>;10 }11 // ERROR: No return in this branch12}13
14component UsesThis() {15 this.foo = 3; // ERROR: Accessing `this` 16 return null;17}
4:10-4:21: Cannot return `new Object()` because: [incompatible-return] Either `Object` [1] is incompatible with `React.Element` [2]. Or `Object` [1] is incompatible with `React.Portal` [3]. Or property `@@iterator` is missing in `Object` [1] but exists in `$Iterable` [4].7:1-7:43: Cannot declare component because component ImplicitReturn [1] is not guaranteed to reach a return statement. An explicit return statement must be included for all possible branches. [component-missing-return]15:3-15:6: Cannot reference `this` from within component declaration [1] [component-this-reference]15:8-15:10: Cannot assign `3` to `this.foo` because property `foo` is missing in global object [1]. [prop-missing]