本项目主要为了学习 flow
语法,所以使用create-react-app创建。创建过程如下:
首先使用创建项目:
create-react-app study-flow && cd study-flow
然后执行了以下操作:
- 运行
yarn add flow-bin
(或npm install --save flow-bin
) - 然后在
package.json
文件中的script
部分加入"flow": "flow"
- 运行
yarn run flow init
// @flow
import React, { Component } from 'react';
type Props = {
text: string,
count?: number,
};
type State = {
count: number,
};
class BasicComponent extends Component<Props, State> {
//...
}
普通的Component
默认接受两个参数,Props
和State
。第二个参数 State
是可选的。默认值是 undefined
。如果不需要,则直接写为
class BasicComponent extends Component<Props> {
//...
}
如果你的 Props
和State
不会重用,也可以直接写在 Component 中。
class BasicComponent extends Component<{text: string},{count: number}> {
//...
}
// @flow
import React, { Component } from 'react';
type Props = {
text: string
};
class DefaultPropsComponent extends Component<Props> {
static defaultProps = {
text: '默认文字'
}
render() {
const { text } = this.props;
return (
<div>
<p>{text}</p>
</div>
);
}
}
export default DefaultPropsComponent;
使用 DefaultPropsComponent
时不需要提供 text
属性。
<DefaultPropsComponent/>
React
支持默认属性的概念,你可以理解为函数的默认参数。flow
当然也支持这个概念。只需要在你的 class
中添加 static defaultProps
即可。
本节源代码
// @flow
import * as React from 'react';
class EventHandlingComponent extends React.Component<{}, { count: number }> {
state = {
count: 0
};
handleClick = (event: SyntheticEvent<HTMLButtonElement>) => {
//使用`event.currentTarget`访问按钮实例
console.log((event.currentTarget: HTMLButtonElement));
this.setState(prevState => ({
count: prevState.count + 1,
}));
};
render() {
return (
<div>
<p>计数: {this.state.count}</p>
<button onClick={this.handleClick}>
增加
</button>
</div>
);
}
}
要输入事件处理程序,可以使用SyntheticEvent <T>
类型。SyntheticEvent
都带有一个类型参数,用来描述时间处理程序 html
类型。
如果你不想带这个参数你也可以,写成SyntheticEvent<>
。React
提供的合成事件与 html 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
// @flow
import * as React from 'react';
class RefComponents extends React.Component<{}> {
// 这里的?是及其重要的,因为你可能并不总是有实例
button: ?HTMLButtonElement;
bindButton = (ref: ?HTMLButtonElement) => this.button = ref;
render() {
return <button ref={this.bindButton}>按钮</button>;
}
}
export default RefComponents;
由于 React
在组件unmonts
(未挂载)的时候调用ref
方法,参数可能为null
。所以直到完成渲染之前,RefComponents
的button
将为undefiend
。所以在button
的描述应为可选的。
本节源代码
// @flow
import * as React from 'react';
type Props = {
children?: React.Node
};
export default class GenerallyComponent extends React.Component<Props> {
render() {
return (
<div>{this.props.children}</div>
);
}
}
一般情况对 child
的描述使用React.Node
即可。重要的一点是你必须使用import * as React from 'react';
替代import React from 'react'
来访问 React.Node
。具体解释见React Type 参考。
本节源代码
// @flow
import * as React from 'react';
const { Component } = React;
class GenerallyComponent extends React.Component<{}> {
//...
}
type Props = {
children: React.ChildrenArray<React.Element<typeof GenerallyComponent>>,
};
export default class SpecificElementComponent extends Component<Props> {
static Item = GenerallyComponent;
render() {
return (
<div>{this.props.children}</div>
);
}
}
有时你只需要一个特定的组件作为子组件。我们将 props
的 type
设置为 React.ChildrenArray<React.Element<typeof GenerallyComponent>>
确保 SpecificElementComponent
只接收 GenerallyComponent
作为子组件。
本节源代码
// @flow
import * as React from 'react';
type Props = {
children: React.Element<any>,
};
export default (props: Props) => (
<div>
{props.children}
</div>
);
有时候你希望你的组件只接收一个children
。这时候你只需要去除 React.ChildrenArray<T>
的 包裹即可。
使用 babel
编译,需要的插件有env
,stage-0
,flow
,react
。详见.babelrc。
如需在编译后还原使用prop-types
则需要添加插件babel-plugin-flow-react-proptypes。