元件語法
元件是建立 React 使用者介面的基礎。雖然元件通常使用 JavaScript 函式表示,但元件語法提供元件原始值,與函式元件相比,具有多項優點,例如
- 語法更簡潔,與函式相比,冗餘和樣板程式碼大幅減少
- 類型系統支援專門針對撰寫 React 而設計
- 更佳支援 React 參照
基本用法
你可以使用元件語法宣告元件,類似於宣告函式的方式
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}
Rest 參數
有時候您不想明確列出每個 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" />;
}
Rest 參數使用物件類型作為註解,這表示您可以使用現有的類型工具程式,例如物件擴充和 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 /> }
Rest 參數可以在不使用 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 呼叫 中,以確保元件在執行階段如預期般運作。參照的一個限制是它們必須定義為內聯參數,不支援 rest 參數中的參照。這是因為需要編譯在 forwardRef
呼叫中,為了讓它正確運作,我們需要能夠從元件定義中靜態地判定參照。
元件的元素
對於在元件語法中宣告的元件,你可以使用 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 可能會在渲染元件時崩潰。 - 元件的所有分支都必須以明確的回傳值結尾。即使
undefined
是有效的回傳值,我們已經看到許多情況,明確的回傳值可以防止在製作環境中出現錯誤。 - 你不能在元件中使用
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]