跳到主要內容

元件語法

元件是建立 React 使用者介面的基礎。雖然元件通常使用 JavaScript 函式表示,但元件語法提供元件原始值,與函式元件相比,具有多項優點,例如

  1. 語法更簡潔,與函式相比,冗餘和樣板程式碼大幅減少
  2. 類型系統支援專門針對撰寫 React 而設計
  3. 更佳支援 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" />

這裡有幾個重要的細節需要注意

  1. 在 Introduction 元件中宣告的 prop 參數名稱與在 JSX 中傳遞給 Introduction 的 prop 名稱相同
  2. 宣告中參數的順序不需要與在 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]

我們不建議需要非常具體的元素類型。這會讓你的父元件和子元件更緊密結合。相反地,此功能旨在讓表達 渲染類型 更容易。

元件規則

元件語法在元件中強制執行一些限制,以幫助確保正確性

  1. 傳回值必須是 React.Node 的子類型,否則 React 可能會在渲染元件時崩潰。
  2. 元件的所有分支都必須以明確的回傳值結尾。即使 undefined 是有效的回傳值,我們已經看到許多情況,明確的回傳值可以防止在製作環境中出現錯誤。
  3. 你不能在元件中使用 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]