渲染类型
某些组件库或设计系统可能想要限制组件的组成方式。例如,菜单应该只将 MenuItems 渲染为子项。渲染类型是支持这些约束的内置方式,同时仍然为用户如何使用这些组件提供了丰富的灵活性。
¥Some component libraries or design systems may want to restrict how components may be composed. For example, a Menu should only ever render MenuItems as children. Render types are a built-in way to support these constraints while still affording users rich flexibility in how they use those components.
基本行为
¥Basic Behavior
组件可以使用 renders 关键字声明它渲染的内容:
¥A component can declare what it renders using the renders keyword:
1import * as React from 'react';2
3component Header(size: string, color: string) { return <div /> }4
5component LargeHeader(color: string) renders Header {6 return <Header size="large" color={color} />; // Ok!7}
当你声明组件渲染某些特定元素时,你可以返回最终在其渲染链中渲染该组件的任何组件:
¥When you declare that your component renders some specific element, you can return any component that eventually renders that component in its renders chain:
1import * as React from 'react';2
3component Header(size: string, color: string) { return <div /> }4
5component LargeHeader(color: string) renders Header {6 return <Header size="large" color={color} />;7}8
9component LargeBlueHeader() renders Header {10 // You could also use `renders LargeHeader` above11 return <LargeHeader color="blue" />;12}
组件可以指定渲染特定元素的 props:
¥Components can specify props that render specific elements:
1import * as React from 'react';2
3component Header(size: string, color: string, message: string) {4 return <h1 style={{color}}>{message}</h1>;5}6
7component Layout(header: renders Header) {8 return (9 <div>10 {header}11 <section>Hi</section>12 </div>13 );14}
你可以将 Header 的元素或渲染 Header
的组件的元素传递给该 prop:
¥And you can pass an element of either Header, or an element of a component that renders Header
, to that prop:
<Layout header={<LargeBlueHeader />} />;
你无法将不渲染标头的组件传递给需要标头的渲染类型:
¥You cannot pass a component that does not render a header to a render type expecting a header:
1import * as React from 'react';2
3component Footer() {4 return <footer />;5}6
7component Header(size: string, color: string, message: string) {8 return <h1 style={{color}}>{message}</h1>;9}10
11component Layout(header: renders Header) {12 return <div>{header}</div>;13}14
15<Layout header={<Footer />} />; // ERROR Footer does not render Header
与设计系统集成
¥Integrating with a design system
渲染类型旨在简化与设计系统的集成。如果设计系统组件中的属性需要渲染类型,你可以将该类型复制/粘贴到组件上以与设计系统集成:
¥Render types are designed to make integrating with a design system simple. If a prop in the design system component expects a render type, you can copy/paste that type onto your component to integrate with the design system:
1import * as React from 'react';2
3component Header() {4 return <h1>Header!</h1>;5}6
7component Layout(header: renders Header) {8 return <div>{header}</div>;9}10
11// Copy-paste the header props' type!12component ProductHeader() renders Header {13 // We must return a value that renders a Header to satisfy the signature14 return <Header />;15}16
17// And now you can integrate with the design system!18<Layout header={<ProductHeader />} />; // OK!
渲染可选元素
¥Rendering Optional Elements
你可能想要描述一个可以接受子组件的组件,该子组件最终可能会渲染一个元素或什么也不渲染。你可以使用专门的渲染类型变体 renders?
来实现此目的:
¥You may want to describe a component that can take a child that may eventually render an element or nothing. You can use a specialized render type variant renders?
to achieve this:
1import * as React from 'react';2
3component DesignSystemCardFooter() {4 return <div>Footer Content</div>;5}6
7component DesignSystemCard(8 children: React.Node,9 footer: renders? DesignSystemCardFooter,10) {11 return <div>{children}{footer}</div>;12}13
14// With these definitions, all of the following work:15
16<DesignSystemCard footer={<DesignSystemCardFooter />}>Card</DesignSystemCard>;17<DesignSystemCard footer={null}>Card</DesignSystemCard>;18<DesignSystemCard footer={undefined}>Card</DesignSystemCard>;19<DesignSystemCard footer={false}>Card</DesignSystemCard>;20
21component ProductFooter(hasFooter?: boolean) renders? DesignSystemCardFooter {22 return hasFooter && <DesignSystemCardFooter />;23}24
25<DesignSystemCard footer={<ProductFooter />}>Card</DesignSystemCard>;
渲染列表
¥Rendering Lists
你可能想要描述一个组件,该组件可以采用任意数量的子组件来渲染特定元素作为 props。你可以使用专门的渲染类型变体 renders*
来实现此目的:
¥You may want to describe a component that can take any amount of children that render a specific element as props. You can use a specialized render type variant renders*
to achieve this:
1import * as React from 'react';2
3component DesignSystemMenuItem() {4 return <li>Menu Item</li>;5}6
7component DesignSystemMenu(8 children: renders* DesignSystemMenuItem,9) {10 return <ul>{children}</ul>11}12
13// With these definitions, all of the following work:14
15const menu1 = (16 <DesignSystemMenu>17 <DesignSystemMenuItem />18 </DesignSystemMenu>19);20
21const menu2 = (22 <DesignSystemMenu>23 <DesignSystemMenuItem />24 <DesignSystemMenuItem />25 </DesignSystemMenu>26);27
28const menu3 = (29 <DesignSystemMenu>30 {[31 <DesignSystemMenuItem />,32 <DesignSystemMenuItem />,33 ]}34 <DesignSystemMenuItem />35 </DesignSystemMenu>36);37
38component ProductMenuItem() renders DesignSystemMenuItem {39 return <DesignSystemMenuItem />;40}41
42const menu4 = (43 <DesignSystemMenu>44 {[45 <ProductMenuItem />,46 <DesignSystemMenuItem />,47 ]}48 <DesignSystemMenuItem />49 </DesignSystemMenu>50);
透明组件
¥Transparent Components
组件可以是 "transparent":
¥Components can be "transparent":
1import * as React from 'react';2
3component TransparentComponent<T: React.Node>(children: T) renders T {4 // .. do something5 return children;6}7
8component Header(text: string) {9 return <h1>{text}</h1>;10}11component InstagramHeader() renders Header {12 return <Header text="Instagram" />;13}14component Layout(15 header: renders Header,16) {17 return <div>{header}</div>;18}19
20component Page() {21 const wrappedHeader = <TransparentComponent><InstagramHeader /></TransparentComponent>22 return <Layout header={wrappedHeader} />; // Ok!23}
与非组件语法组件的互操作
¥Interop with non-Component-Syntax components
你也可以使用 renders
来注释函数组件:
¥You can use renders
to annotate function components as well:
1import * as React from 'react';2
3component Header(text: string) {4 return <h1>{text}</h1>;5}6component InstagramHeader() renders Header {7 return <Header text="Instagram" />;8}9component Layout(10 header: renders Header,11) {12 return <div>{header}</div>;13}14
15function FunctionHeader(): renders Header {16 return <InstagramHeader />;17}18
19function InstagramPage() {20 return <Layout header={<FunctionHeader />} />; // OK!21}
子类型化行为
¥Subtyping Behavior
所有渲染类型都是 React.Node
的子类型,只有 renders Foo
是 React.MixedElement
的子类型。
¥All render types are subtypes of React.Node
, and only renders Foo
is subtype of React.MixedElement
.
1import * as React from 'react';2
3component Header() {4 return <h1>Hello Header!</h1>;5}6
7declare const rendersHeader: renders Header;8declare const rendersMaybeHeader: renders? Header;9declare const rendersHeaderList: renders* Header;10
11rendersHeader as React.Node;12rendersMaybeHeader as React.Node;13rendersHeaderList as React.Node;14
15rendersHeader as React.MixedElement;16rendersMaybeHeader as React.MixedElement; // ERROR!17rendersHeaderList as React.MixedElement; // ERROR!
renders Foo
是 renders? Foo
的亚型,renders? Foo
是 renders* Foo
的亚型。
¥renders Foo
is a subtype of renders? Foo
, and renders? Foo
is a subtype of renders* Foo
.
1import * as React from 'react';2
3component Header() {4 return <h1>Hello Header!</h1>;5}6
7declare const rendersHeader: renders Header;8declare const rendersMaybeHeader: renders? Header;9declare const rendersHeaderList: renders* Header;10
11rendersHeader as renders? Header;12rendersHeader as renders* Header;13rendersMaybeHeader as renders* Header;14
15rendersMaybeHeader as renders Header; // ERROR16rendersHeaderList as renders Header; // ERROR17rendersHeaderList as renders? Header; // ERROR