组件语法
组件 是构建 React 中 UI 的基础。虽然组件通常使用 JavaScript 函数来表达,但组件语法提供了组件基本值,与函数组件相比,它具有几个优势,例如
- 更优雅的语法,比函数更简洁,减少了样板代码
- 专门为编写 React 量身定制的类型系统支持
- 对 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" />
。
这里有一些重要的细节需要注意
- 在 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,因为您不打算在组件中单独引用它们。当您编写一个包装另一个组件的组件,并且需要将 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]
我们不建议要求非常具体的元素类型。这将使您的父组件和子组件更加耦合。相反,此功能旨在使表达 渲染类型 更容易。
组件规则
组件语法在组件中强制执行一些限制,以帮助确保正确性
- 返回值必须是
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]