跳至主要内容

组件语法

组件 是构建 React 中 UI 的基础。虽然组件通常使用 JavaScript 函数来表达,但组件语法提供了组件基本值,与函数组件相比,它具有几个优势,例如

  1. 更优雅的语法,比函数更简洁,减少了样板代码
  2. 专门为编写 React 量身定制的类型系统支持
  3. React refs 的更好支持

基本用法

您可以使用与声明函数类似的方式使用组件语法声明组件

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}

剩余参数

有时您不想显式列出每个 prop,因为您不打算在组件中单独引用它们。当您编写一个包装另一个组件的组件,并且需要将 props 从您的组件传递给内部组件时,这很常见

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 参数

要访问组件中的 refs,您只需要添加一个 ref 参数。

1import * as React from 'react';2
3component ComponentWithARef(ref: React.RefSetter<HTMLElement>) {4  return <div ref={ref} />;5}

在幕后,组件语法将用必要的 React.forwardRef 调用 包装组件,以确保组件在运行时按预期工作。refs 的唯一限制是它们必须定义为内联参数,剩余参数中的 refs 不受支持。这是因为需要编译 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]

我们不建议要求非常具体的元素类型。这将使您的父组件和子组件更加耦合。相反,此功能旨在使表达 渲染类型 更容易。

组件规则

组件语法在组件中强制执行一些限制,以帮助确保正确性

  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]