學習如何在Flow中使用React
將Flow類型添加到React組件后,Flow將靜態地確保你按照組件被設計的方式開發。在React早期,該庫提供了執行基本運行時檢查的PropType。而Flow的強大之處在于它在你運行代碼前告訴你哪里出了問題。
如果你想同時進行靜態和運行時檢查,有一些Babel插件可以做到將Flow類型生成PropTypes,例如babel-plugin-react-flow-props-to-prop-types。
組件(Components)
學習如何在Flow中使用React類組件和無狀態函數組件
類組件
在我們展示如何使用Flow鍵入React類組件之前,讓我們先展示如何編寫一個沒有 Flow 的React類組件,但使用React的prop類型。你將擴展React.Component
并添加一個靜態propTypes
屬性。
import React from 'react';
import PropTypes from 'prop-types';
class MyComponent extends React.Component {
static propTypes = {
foo: PropTypes.number.isRequired,
bar: PropTypes.string,
};
render() {
return <div>{this.props.bar}</div>;
}
}
現在,在我們剛才寫的組件中引入flow:
import * as React from 'react';
type Props = {
foo: number,
bar?: string,
};
class MyComponent extends React.Component<Props> {
render() {
this.props.doesNotExist; // Error! You did not define a `doesNotExist` prop.
return <div>{this.props.bar}</div>;
}
}
<MyComponent foo={42} />;
我們刪除了對prop-types
的依賴,并添加了一個名為Props的Flow對象類型,其形式與prop類型相同,但是使用了Flow的靜態類型語法。 然后我們將新的Props類型作為類型參數傳遞給React.Component。
現在如果你嘗試在<MyComponent>中使用一個類型為string
的foo(類型是number
),將提示錯誤。無論我們在React組件中何處使用this.props,Flow都會將其視為我們定義的Props類型。
注意:如果你不需要再次使用Props類型,可以用內聯方式定義:
extends React.Component <{foo: number, bar?: string}>
注意: 我們在這里導入
React
作為命名空間,import * as React from "react"
,而不是使用默認導入,import React from "react"
。在將React作為ES模塊導入時,可以使用任何一種樣式,但作為命名空間導入方便你訪問React的公共類型。
React.Component<Props, State>
是一個帶有兩個類型參數泛型類型,Props
和State
。第二個參數State
是可選的,默認情況下它是未定義的,所以你可以在上面的例子中看到我們沒有包含State
。
添加狀態
現在為React類組件的State
創建一個新的對象類型,在下面的例子中我們將其命名為State,并將其作為第二個類型的參數傳遞給React.Component
。
import * as React from 'react';
type Props = { /* ... */ };
type State = {
count: number,
};
class MyComponent extends React.Component<Props, State> {
state = {
count: 0,
};
componentDidMount() {
setInterval(() => {
this.setState(prevState => ({
count: prevState.count + 1,
}));
}, 1000);
}
render() {
return <div>Count: {this.state.count}</div>;
}
}
<MyComponent />;
在上面的例子中,我們使用了一個React的setState()
更新函數,但是你也可以把一個局部的狀態對象傳給setState()。
注意:如果你不需要再次使用
State
類型,可以用內聯方式定義:extends React.Component <{}, {count: number}>
使用默認Props
React支持defaultProps
的概念,你可以把它看作默認的函數參數。當你創建一個元素并且你沒有包含一個默認的props
時,React將會從defaultProps
中獲取相應的值。Flow
也支持這個概念。要使用defaultProps
,則在類中添加static defaultProps
。
import * as React from 'react';
type Props = {
foo: number, // 必須
};
class MyComponent extends React.Component<Props> {
static defaultProps = {
foo: 42, // ...默認的foo
};
}
// 不傳入foo也無妨
<MyComponent />
Flow會從靜態的defaultProps
中推斷出你的默認props
的類型,所以你不需要添加任何類型的注釋來使用defaultProps
。
注意:你不需要在你的
Props
類型中設置foo
為可選的。如果你的默認Props
中有foo,Flow將確保foo是可選的。
無狀態函數組件
除了類,React還支持無狀態的函數組件。你可以像鍵入一個函數一樣鍵入這些組件:
import * as React from 'react';
type Props = {
foo: number,
bar?: string,
};
function MyComponent(props: Props) {
props.doesNotExist; // Error! You did not define a `doesNotExist` prop.
return <div>{props.bar}</div>;
}
<MyComponent foo={42} />
無狀態函數組件中使用默認Props
與類組件類似,React的無狀態函數組件也支持defautProps
。無狀態函數組件的默認Props將無需任何額外的類型注釋。
import * as React from 'react';
type Props = {
foo: number, // 必須
};
function MyComponent(props: Props) {}
MyComponent.defaultProps = {
foo: 42, // 默認foo
};
// 不傳入foo也無妨
<MyComponent />;
事件處理(Event Handling)
Flow中React事件處理的類型和最佳實踐
在React文檔“處理事件”提供了一些關于如何定義事件處理程序的不同建議。如果你正在使用Flow,建議使用屬性初始值設定項語法,因為相對靜態類型最簡單。 屬性初始值設定器的語法如下所示:
class MyComponent extends React.Component<{}> {
handleClick = event => { /* ... */ };
}
合成事件(SyntheticEvent)
一個跨瀏覽器原生事件包裝器。 它具有與瀏覽器原生事件相同的接口,包括 stopPropagation() 和 preventDefault() ,除了事件在所有瀏覽器中他們工作方式都相同。
編寫事件處理程序中你可能會用到SyntheticEvent<T>
類型,如下所示:
import * as React from 'react';
class MyComponent extends React.Component<{}, { count: number }> {
handleClick = (event: SyntheticEvent<HTMLButtonElement>) => {
// 使用 event.currentTarget訪問button 實例
// (event.currentTarget: HTMLButtonElement);
this.setState(prevState => ({
count: prevState.count + 1,
}));
};
render() {
return (
<div>
<p>Count: {this.state.count}</p>
<button onClick={this.handleClick}>
Increment
</button>
</div>
);
}
}
還有更多特定的合成事件類型,如SyntheticKeyboardEvent <T>
,SyntheticMouseEvent <T>
或SyntheticTouchEvent <T>
。 SyntheticEvent <T>
類型都采用單一類型的參數。事件處理程序所在的HTML元素的類型。
如果你不想添加你的元素實例的類型,你也可以使用SyntheticEvent
,而不需要像這樣的類型參數:SyntheticEvent <>
。
注意:為了得到元素實例,如上例中的HTMLButtonElement,使用event.target而不是event.currentTarget是一個常見的錯誤。 你想使用event.currentTarget的原因是event.target可能是錯誤的元素,由于事件冒泡。
注意:React使用自己的事件系統,因此使用SyntheticEvent類型而不是Event,KeyboardEvent和MouseEvent等DOM類型非常重要。
React提供的SyntheticEvent <T>
類型和它們相關的DOM事件是:
-
SyntheticEvent<T>
--- Event -
SyntheticAnimationEvent<T>
--- AnimationEvent -
SyntheticCompositionEvent<T>
--- CompositionEvent -
SyntheticInputEvent<T>
--- InputEvent -
SyntheticUIEvent<T>
--- UIEvent -
SyntheticFocusEvent<T>
--- FocusEvent -
SyntheticKeyboardEvent<T>
--- KeyboardEvent -
SyntheticMouseEvent<T>
--- MouseEvent -
SyntheticDragEvent<T>
--- DragEvent -
SyntheticWheelEvent<T>
--- WheelEvent -
SyntheticTouchEvent<T>
--- TouchEvent -
SyntheticTransitionEvent<T>
--- TransitionEvent
ref 函數(ref functions)
學習如何在flow中使用ref 函數
React允許你用ref函數獲取某個元素或組件的實例。要使用ref函數,可以在你的類中添加一個可選的實例類型,并將你的實例分配給你的ref函數中的該屬性。
import * as React from 'react';
class MyComponent extends React.Component<{}> {
button: ?HTMLButtonElement;
render() {
return <button ref={button => (this.button = button)}>Toggle</button>;
}
}
在上面的例子中,ref的第一個參數是HTMLButtonElement | null
,因為當組件卸載時,React將用null調用你的ref回調。另外,在React完成渲染之前,MyComponent上的button
屬性將不會被設置。在那之前你的按鈕引用將是未定義的。使用可選標志?
(如在?HTMLButtonElement
)避免引用異常。
Children
學習如何使用flow嚴格規范組件接收的子元素
React元素可以有零個或多個子元素。使用Flow描述這些子元素讓你能夠構建富有表現力的React API。當為React組件的子項添加類型時,需要考慮的類型是React.Node。
import * as React from 'react';
type Props = {
children?: React.Node,
};
function MyComponent(props: Props) {
return <div>{props.children}</div>;
}
注意:這里使用import * as React from 'react'
來訪問React.Node
類型而不是import React from 'react'
。 我們在后續的React Type Reference
中解釋為什么。
然而,如果你想用React children API
做更強大的事情,那么你需要對如何使用它有所了解。先從一個沒有chidlren
組件的例子開始,讓我們先看看幾個案例。(已經了解的同學可以跳過這一塊)
<MyComponent />;
// 等同于...
React.createElement(MyComponent, {});
如果不傳入任何子元素,則不會設置props.children。 如果你嘗試訪問props.children,它將是undefine
的。
<MyComponent>{42}</MyComponent>;
// 等同于...
React.createElement(MyComponent, {}, 42);
如果傳遞一個單一的值,那么props.children
就是那個單一的值。這里props.children
將是數字42。重要的是,props.children
不會是一個數組它的值是42。
<MyComponent>{1}{2}</MyComponent>;
// 等同于...
React.createElement(MyComponent, {}, 1, 2);
或者
<MyComponent>{'hello'} world</MyComponent>;
// 注意這里world前面的空格
// 等同于...
React.createElement(MyComponent, {}, 'hello', ' world');
亦或者
<MyComponent>{'hello'} <strong>world</strong></MyComponent>;
// 注意這里<strong>前面的空格
// <MyComponent>
// {'hello'}
// <strong>world</strong>
// </MyComponent>
// 換成這樣寫后換行符后的換行符和縮進會被刪除,所以這里這樣寫不會有空格。
// 等同于...
React.createElement(
MyComponent,
{},
'hello',
' ',
React.createElement('strong', {}, 'world'),
);
現在傳遞兩個或多個值,props.children
是一個數組,上面的例子中,它的值可能是[1, 2]
或者['hello', 'world']
或者['hello', ' ', <strong>world</strong>]
這里思考一個問題,如果我們的children
是一個數組,那么props.children
是一個二維數組嗎?不是的。
<MyComponent>{[1, 2]}</MyComponent>;
// 等同于...
React.createElement(MyComponent, {}, [1, 2]);
與傳遞單一值的規則一樣,盡管[1,2]是一個數組,它是一個單一的值,所以props.children
就是這個值。這就是說props.children
將是數組[1,2]
而不是一個二維數組。
同樣的,當你使用array.map()
傳遞一個數組時也是相當于傳遞單一值。
<MyComponent>
{messages.map(message => <strong>{message}</strong>)}
</MyComponent>
// 等同于...
React.createElement(
MyComponent,
{},
messages.map(message => React.createElement('strong', {}, message)),
);
但是,如果是多個數組元素并且是獨立的,那將會發生什么?
<MyComponent>{[1, 2]}{[3, 4]}</MyComponent>;
// 等同于...
React.createElement(MyComponent, {}, [1, 2], [3, 4]);
這里props.children
將是一個二維數組。具體值將是[[1,2],[3,4]]
。
React children
的規則是,如果傳遞值,那么props.children
不會被設置,如果傳遞單一的值,那么props.children
將被設置為正確的值,如果傳遞多個值,props.children
將是這些值的新數組。
注意
<MyComponent>
// some comment...
{42}
</MyComponent>
這里會編譯成:React.createElement(MyComponent, {}, '// some comment...', 42)
,這時候prop.children
的值為['// some comment...', 42]
。在JSX中使用注釋需遵循下面的語法:
<MyComponent>
{/* some comment... */}
{42}
</MyComponent>
只允許接收特定子元素
有時候你想你的React組件只接收特定的組件作為子元素。這通常發生構建一個需要特定列子組件的表格組件或者需要為每個選項卡進行特定配置的選項卡欄時。比如,React Native的<TabBarIOS>
標簽欄組件就是使用這種模式模式。
React Native的<TabBarIOS>
組件只允許React元素子元素,而且這些元素必須具有<TabBarIOS.Item>
的組件類型。如:
<TabBarIOS>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
</TabBarIOS>
而當我們用以下的方式使用<TabBarIOS>
時則會拋出異常:
<TabBarIOS>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<View>{/* ... */}</View>
<SomeOtherComponent>{/* ... */}</SomeOtherComponent>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
</TabBarIOS>
所以flow是如何幫助我們阻止這種不規范的用法?
import * as React from 'react';
class TabBarIOSItem extends React.Component<{}> {
// implementation...
}
type Props = {
children: React.ChildrenArray<React.Element<typeof TabBarIOSItem>>,
};
class TabBarIOS extends React.Component<Props> {
static Item = TabBarIOSItem;
// implementation...
}
<TabBarIOS>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
<TabBarIOS.Item>{/* ... */}</TabBarIOS.Item>
</TabBarIOS>;
我們將props
中children
的類型設置為React.ChildrenArray <React.Element <typeof TabBarIOSItem>>
,這將保證<TabBarIOS>
只能接收TabBarIOS.Item
類型的React元素。
注意:如果想使用
map()
和forEach()
方法或想跟處理JavaScript數組一樣處理數組React.ChildrenArray<T>
,React在React.Children API
中提供了React.Children.toArray()
方法使你可以像JavaScript一樣處理數組React.ChildrenArray<T>
。
強制只接收一個子元素。
有時候你想強制你的組件只接收一個子元素。你可以使用React.Children.only()
函數來約束這種行為,但是你也可以使用Flow實現同樣的效果。現在你不能再使用React.ChildrenArray<T>包裹并定義你子元素的類型:
import * as React from 'react';
type Props = {
children: React.Element<any>,
};
function MyComponent(props: Props) {...}
// 拋異常! 必須傳入子元素
<MyComponent />;
// 拋異常! 傳入了兩個或多個子元素
<MyComponent>
<div />
<div />
<div />
</MyComponent>;
// 正常,傳入一個子元素
<MyComponent>
<div />
</MyComponent>;
React允許你傳遞任何值來作為React組件的子項:
<MyComponent>
{data => (
<div>{data.foo}</div>
)}
</MyComponent>
react-router
版本4允許接收一個函數作為<Route>
組件的子元素。
<Route path={to}>
{({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest}/>
</li>
)}
</Route>
如何在Flow中使用<Route>組件:
import * as React from 'react';
type Props = {
children: (data: { match: boolean }) => React.Node,
path: string,
// 其它props...
};
class Route extends React.Component<Props> {...}
<Route path={to}>
{({ match }) => (
<li className={match ? 'active' : ''}>
<Link to={to} {...rest}/>
</li>
)}
</Route>;
children
的類型是一個函數,它接收一些對象類型并返回一個React.Node
,React.Node
是React可以渲染的任何值的類型。一個子函數不需要返回React.Node
。它可以返回任何類型,但是在這種情況下,react-router
需要渲染由children
函數返回的結果。
React.Node
是chidlren
的一般類型,但有時你可能想要使用React.Node
,同時排除一些像字符串和數字的基本類型。例如,React Native <View>
組件就執行了此操作。
React Native <View>
組件將允許任何原始值或任何React元素作為其子元素。 但是,<View>
不允許字符串或數字作為子項! 你可以使用React.Node
作為<View>
的子類型,但是React.Node
包含了我們不希望用于<View>
的字符串。 所以我們需要創建自己的類型。
import * as React from 'react';
type ReactNodeWithoutStrings = React.ChildrenArray<
| void
| null
| boolean
| React.Element<any>
>;
type Props = {
children?: ReactNodeWithoutStrings,
// other props...
};
class View extends React.Component<Props> {
// implementation...
}
React.ChildrenArray<T>
是為React嵌套數組數據結構建模的一種類型。 ReactNodeWithoutStrings
使用React.ChildrenArray <T>
作為null,boolean或React元素的任意嵌套數組。
React.Element <typeof Component>
是React元素的類型,如<div />
或<MyComponent />
。 值得注意的是元素和組件不一樣!
高階組件(Higher-order Components)
學習如何在flow中使用React高階組件
高階組件模式是React中流行的一種模式。如果你還不知道更高級的組件是什么,那么請確保在繼續之前閱讀高階組件的React文檔。
要學習如何使用更高階的組件,我們先看看Recompose
中有關高階組件的例子。Recompose
是一個流行的React庫,提供了許多更高階的組件,其作者也是Redux
的編寫者。下面是Recompose
中關于mapProps()
高階組件的部分實現。
mapProps()
接收一個函數,其改變傳入值后返回新的值:
function MyComponent({ bar }: { bar: number }) {
return <div>{bar}</div>;
}
const MyEnhancedComponent = mapProps(
({ foo }) => ({ bar: foo + 1 }), // 接收原始bar的值,返回加1后的新的bar值
)(MyComponent);
<MyEnhancedComponent foo={1} />; // bar的值為2
我們將使用React.ComponentType<Props>
來定義MyComponent
類型和MyEnhancedComponent
類型。 React.ComponentType<Props>
是無狀態功能組件和類組件的聯合,其中Props
是組件的props
的定義類型。
我們希望mapProps()
返回一個函數,該函數將React組件作為其第一個也是唯一的參數,并返回一個React組件。
import * as React from 'react';
function mapProps(): (React.ComponentType<any>) => React.ComponentType<any> {
return Component => {
// implementation...
};
}
上面我們使用了React.ComponentType<any>
定義類型!接下來我們將使用范型函數類型來代替any類型。
import * as React from 'react';
function mapProps<PropsInput: {}, PropsOutput: {}>(
// TODO
): (React.ComponentType<PropsOutput>) => React.ComponentType<PropsInput> {
return Component => {
// implementation...
};
}
PropsInput
和PropsOutput
綁定了{}。這意味著PropsInput
和PropsOutput
必須是對象類型,所以在mapProps()
的實現中如果你類型非對象將無法傳遞PropsInput或PropsOutput。
現在,為mapperFn
添加類型,它接收PropsInput
并返回PropsOutput
給mapProps()
。
import * as React from 'react';
function mapProps<PropsInput: {}, PropsOutput: {}>(
mapperFn: (PropsInput) => PropsOutput,
): (React.ComponentType<PropsOutput>) => React.ComponentType<PropsInput> {
return Component => {
// implementation...
};
}
現在你可以放心地使用mapProps()
來確保你的類型是正確的。
用高階組件注入Props
高階組件的常見用法是注入一個prop
。我們先看看不注入props
的高階組件寫法:
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<Props>,
): React.ComponentType<Props> {
// implementation...
}
這個泛型函數接收一個React組件,返回一個React組件。 要從返回的組件中刪除一個prop
,我們可以使用$Diff
。
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<Props>,
): React.ComponentType<$Diff<Props, { foo: number | void }>> {
// implementation...
}
這里使用$Diff
來表示props
的類型,并返回除number
類型的foo
外的Props
中的所有東西。
注意:如果foo不存在
Props
中會拋出異常,$Diff<{},{foo: number}>
將拋出異常。所以需要與void的聯合$Diff <{}, {foo: number | void}>
。一個可選的prop
不會完全刪除foo
。$Diff <{foo: number}, {foo?: number}>
。
現在我們可以使用injectProp()
來注入foo
。
import * as React from 'react';
function injectProp<Props: {}>(
Component: React.ComponentType<Props>,
): React.ComponentType<$Diff<Props, { foo: number | void }>> {
return function WrapperComponent(props: Props) {
return <Component {...props} foo={42} />;
};
}
class MyComponent extends React.Component<{
a: number,
b: number,
foo: number,
}> {}
const MyEnhancedComponent = injectProp(MyComponent);
// 盡管MyComponent需要foo,但是這里不是必須傳入.
<MyEnhancedComponent a={1} b={2} />;
使用React.ElementConfig<>支持defaultProps
到目前為止,我們編寫的高階組件都必須有defaultProps
。為了保留defaultProps
的可選性,你可以使用React.ElementConfig<typeof Component>
。
function myHOC<Props, Component: React.ComponentType<Props>>(
WrappedComponent: Component
): React.ComponentType<React.ElementConfig<Component>> {
return props => <WrappedComponent {...props} />;
}
Redux
Redux有三個主要部分需要使用flow定義:
- State
- Actions
- Reducers
Redux state
定義State
與定義其它對象是一樣的
type State = {
users: Array<{
id: string,
name: string,
age: number,
phoneNumber: string,
}>,
activeUserID: string,
// ...
};
確保Redux state不變性
Redux state 本身意味著不可變:創建一個新的狀態對象,而不是改變單個對象的屬性。
你可以通過在整個狀態對象中使用“協變”屬性將每個屬性有效地設置為“只讀”來強制執行此操作。
type State = {
+users: Array<{
+id: string
+name: string,
+age: number,
+phoneNumber: string,
}>,
+activeUserID: string,
// ...
};
而當你嘗試改變state
中的屬性值時,flow
會拋出異常
// @flow
type State = {
+foo: string
};
let state: State = {
foo: "foo"
};
state.foo = "bar"; // Error!
Redux actions
Redux actions的基本類型是一個帶type
屬性的對象
type Action = {
+type: string,
};
你可能想要為actions
定義更具體的類型,那么使用不相交的聯合和每個單獨的類型。這樣flow
可以更好地理解reducer
。
type Action =
| { type: "FOO", foo: number }
| { type: "BAR", bar: boolean }
| { type: "BAZ", baz: string };
Redux creators
// @flow
type FooAction = { type: "FOO", foo: number };
type BarAction = { type: "BAR", bar: boolean };
type Action =
| FooAction
| BarAction;
function foo(value: number): FooAction {
return { type: "FOO", foo: value };
}
function bar(value: boolean): BarAction {
return { type: "BAR", bar: value };
}
Redux thunk actions
使用Redux thunk actions需要為ThunkAction
添加函數Dispatch
和GetState
。GetState
是一個返回Object的函數。 Dispatch
接收Action,ThunkAction,PromiseAction和Array <Action>
的不相交聯合,并可以返回any
。
type Dispatch = (action: Action | ThunkAction | PromiseAction) => any;
type GetState = () => State;
type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
type PromiseAction = Promise<Action>;
然后編寫thunk action creator,將ThunkAction的返回類型添加到creator
中。
type Action =
| { type: "FOO", foo: number }
| { type: "BAR", bar: boolean };
type GetState = () => State;
type PromiseAction = Promise<Action>;
type ThunkAction = (dispatch: Dispatch, getState: GetState) => any;
type Dispatch = (action: Action | ThunkAction | PromiseAction | Array<Action>) => any;
function foo(): ThunkAction {
return (dispatch, getState) => {
const baz = getState().baz
dispatch({ type: "BAR", bar: true })
doSomethingAsync(baz)
.then(value => {
dispatch({ type: "FOO", foo: value })
})
}
}
Redux reducers
Reducers接收并合并傳入的狀態和行為。
function reducer(state: State, action: Action): State {
// ...
}
你還可以default
中使用空類型來驗證你是否處理了每種類型的操作。
// @flow
type State = { +value: boolean };
type FooAction = { type: "FOO", foo: boolean };
type BarAction = { type: "BAR", bar: boolean };
type Action = FooAction | BarAction;
function reducer(state: State, action: Action): State {
switch (action.type) {
case "FOO": return { ...state, value: action.foo };
case "BAR": return { ...state, value: action.bar };
default:
(action: empty);
return state;
}
}
參考文章
- Using Redux with Flow - Alex Kotliarskyi
- Redux and Flowtype - Christian de Botton
類型參考(Type Reference)
由React模塊導出的所有公共Flow類型的參考。
當使用高級React模式時,React會導出一些可能對你有用的公共類型。以下是每種類型的完整參考,以及一些如何使用它們的例子。
目錄:
React.Node
React.Element<typeof Component>
React.ChildrenArray<T>
React.ComponentType<Props>
React.StatelessFunctionalComponent<Props>
React.ElementType
React.Key
React.Ref<typeof Component>
React.ElementProps<typeof Component>
React.ElementConfig<typeof Component>
React.ElementRef<typeof Component>
這些類型導出全部來自React模塊。 如果你想訪問React對象的成員(例如React.Node
或React.StatelessFunctionalComponent
),并且將React作為ES模塊導入,那么你應該把React作為一個命名空間導入:
import * as React from 'react';
你也可以使用CommonJS的方式導入React
const React = require('react');
你還可以在ES模塊環境或CommonJS環境中使用命名的類型導入:
import type {Node} from 'react';
注意:當我們使用默認導入的方式導入React:
import React from 'react';
你將有權訪問React導出的所有值,但是無法訪問目錄列出的類型!Flow不會將類型添加到默認導出,因為默認導出可以是任何值(如數字)。Flow會將導出的命名類型添加到ES命名空間對象,你可以通過
import * as React from 'react'
從react
獲取,因為Flow知道你是否導出了與導出類型具有相同名稱的值。除了這種方式,你還可以使用命名類型去訪問具體類型如:import type {Node} from 'react'
。
React.Node
這表示React可以渲染的任何節點。React.Node
可以是undefined,null
,布爾值,數字,字符串,React元素或可遞歸數組。
class MyComponent extends React.Component<{}> {
render(): React.Node {
// ...
}
}
function MyComponent(props: {}): React.Node {
// ...
}
這里對render()
方法或無狀態函數組件的返回類型的定義不是必須的。
下面是React.Node
作為子類型的示例:
function MyComponent({ children }: { children: React.Node }) {
return <div>{children}</div>;
}
所有react-dom
JSX內部函數都有React.Node作為它們的子類型。 <div>,<span>
和其他所有內容。React.Node
的定義可以粗略地等同于React.ChildrenArray<T>
:
type Node = React.ChildrenArray<void | null | boolean | string | number | React.Element<any>>;
React.Element<typeof Component>
React元素是JSX元素的值的類型:
const element: React.Element<'div'> = <div />;
React.Element<typeof Component>
是React.createElement()
的返回類型。
React.Element <typeof Component>
接收一個單一的類型參數,typeof Component
。 typeof Component
是React元素的組件類型。對于一個內部元素,typeof Component
將是你使用的內在的字符串文字。以下是一些示例:
(<div />: React.Element<'div'>); // OK
(<span />: React.Element<'span'>); // OK
(<div />: React.Element<'span'>); // Error: div is not a span.
typeof Component
也可以React 組件或無狀態函數組件
class Foo extends React.Component<{}> {}
function Bar(props: {}) {}
(<Foo />: React.Element<typeof Foo>); // OK
(<Bar />: React.Element<typeof Bar>); // OK
(<Foo />: React.Element<typeof Bar>); // Error: Foo is not Bar
React.ChildrenArray<T>
React children數組可以是單個值或嵌套到任何級別的數組。它被設計來與React.Children API
一起使用。
例如,如果你想從React.ChildrenArray<T>
獲得一個正常的JavaScript數組,請看下面的例子:
import * as React from 'react';
// 單個值
const children: React.ChildrenArray<number> = 42;
// 任何嵌套級別的數組
const children: React.ChildrenArray<number> = [[1, 2], 3, [4, 5]];
// 使用`React.Children` API 展開數組
const array: Array<number> = React.Children.toArray(children);
React.ComponentType<Props>
這是類組件或無狀態函數組件的聯合。這是你要用于接收或返回React組件(如高階組件或其他公共組件)的函數的類型。
如何使用React.ComponentType<Props>
和React.Element <typeof Component>
來構造一個具有一組特定props
的組件:
type Props = {
foo: number,
bar: number,
};
function createMyElement<C: React.ComponentType<Props>>(
Component: C,
): React.Element<C> {
return <Component foo={1} bar={2} />;
}
React.ComponentType<Props>
不包含像div
或span
這樣的內在的JSX元素類型。React.ComponentType<Props>
的定義大致是:
type ComponentType<Props> =
| React.StatelessFunctionalComponent<Props>
| Class<React.Component<Props, any>>;
React.StatelessFunctionalComponent<Props>
無狀態函數組件的類型
type StatelessFunctionalComponent<Props> =
(props: Props) => React.Node;
React.ElementType
與React.ComponentType<Props>
類似,除了它也包含JSX內在函數(字符串)。
type ElementType =
| string
| React.ComponentType<any>;
React.Key
React元素的key
值類型。它是一個字符串和數字的聯合,定義如下:
type Key = string | number;
React.Ref<typeof Component>
React元素上的ref
屬性的類型。React.Ref<typeof Component>
可以是一個字符串或ref函數。ref函數
將接收一個唯一的參數,它將是使用React.ElementRef<typeof Component>
檢索的元素實例,或者是null
,因為React在卸載時會將null
傳遞給ref函數。與React.Element<typeof Component>
一樣,typeof Component
必須是React組件的類型。React.Ref<typeof Component>
的定義大致是:
type Ref<C> =
| string
| (instance: React.ElementRef<C> | null) => mixed;
React.ElementProps<typeof Component>
獲取React元素類型的props
。類型可以是React類組件,無狀態功能組件或JSX內部字符串。此類型用于React.Element<typeof Component>
上的props屬性。與React.Element<typeof Component>
一樣,必須是React組件的類型
React.ElementConfig<typeof Component>
如React.ElementProps<typeof Component>
,此方法獲取除defaultProps
外的組件的props
的類型。
import * as React from 'react';
class MyComponent extends React.Component<{foo: number}> {
static defaultProps = {foo: 42};
render() {
return this.props.foo;
}
}
// `React.ElementProps<>` 需要有`foo`值即使本身是 `defaultProp`.
({foo: 42}: React.ElementProps<typeof MyComponent>);
// `React.ElementConfig<>` 不需要 `foo` 值,因為本身就是`defaultProp`.
({}: React.ElementConfig<typeof MyComponent>);
與React.Element<typeof Component>
一樣,必須是React組件的類型。
React.ElementRef<typeof Component>
獲取React元素的實例類型。對于各種組件類型,實例會有所不同:
- React類組件將是類實例。所以如果你有類Foo繼承
React.Component<{}>{}
并使用React.ElementRef<typeof Foo>
,那么類型將是Foo的實例。 - React無狀態函數組件沒有返回實例,所以
React.ElementRef <typeof Bar>
(當Bar是bar(){})時,會給你undefined
的類型。 - 像
div
的JSX內聯函數會返回DOM實例。React.ElementRef<'div'>
是HTMLDivElement
。React.ElementRef<'input'>
將會是HTMLInputElement
。
與React.Element<typeof Component>一樣,必須是React組件的類型。