React作为facebook的亲儿子,flow自然会鼎力配合React的工作,不需要任何额外的配置,flow已经很完整地支持React的类型系统。
好处
引入声明系统对React组件而言,有诸多好处
- 类型声明比
PropTypes
声明要便捷的多 - 约束
props
和state
类型和操作 - 事件处理类型检查
> 尽管React提供了prop-types
库可以为组件添加运行时参数检查能力,但是需要在运行后才能发现问题,体验和查错能力上远逊于静态类型检查。
使用
声明组件
过去,官方建议是使用prop-types
加入检查。
1 | import React from 'react'; |
在编写代码时,直到代码运行之前,我们并不知道会不会出错。
下面是在引入flow后的写法
1 | // @flow |
可以看到,不仅在代码上简练了很多,可读性也更强,并且在编写过程中,flow已经告诉了我们错误。
如果想限定传入的props
,防止父组件传入额外的无用属性,只需要将Props
对象类型声明成完全匹配类型即可。如在某些情况使用将...
赋值在一个img
节点上,为了避免传入了未知的img
节点属性(react运行时会抛错),可以限制此组件传入参数。
1 | // @flow |
同样的,下面对state
加入检查。
1 | // @flow |
在组件中任何地方访问未声明的state属性,或者传入了错误的state字段,flow都会抛出错误。
除了类组件,React还有函数组件,函数组件没有state
,声明就更简单了。
1 | // @flow |
默认属性
React支持对props
设置默认属性。如果某个字段有默认属性,它就自动变成可选字段。
1 | // @flow |
对函数组件也是如此
1 | // @flow |
事件处理
在flow中推荐使用箭头函数属性的方式定义事件回调函数,这也是React中定义事件回调最方便的方式。
在回调函数参数中对参数event
加入SyntheticEvent<T>
声明。
1 | import * as React from 'react'; |
如果想要对事件的详细类型,flow提供了下面几种事件类型。
SyntheticEvent<T>
for EventSyntheticAnimationEvent<T>
for AnimationEventSyntheticCompositionEvent<T>
for CompositionEventSyntheticInputEvent<T>
for InputEventSyntheticUIEvent<T>
for UIEventSyntheticFocusEvent<T>
for FocusEventSyntheticKeyboardEvent<T>
for KeyboardEventSyntheticMouseEvent<T>
for MouseEventSyntheticDragEvent<T>
for DragEventSyntheticWheelEvent<T>
for WheelEventSyntheticTouchEvent<T>
for TouchEventSyntheticTransitionEvent<T>
for TransitionEvent
ref
首先要吐槽一下
官方文档居然没更新V16的新的ref
语法声明方式!!
老版的回调函数:
1 | import * as React from 'react'; |
那么新版的呢,只有自己动手了。稍微研究一下会发现,React.createRef
其实是一个泛型方法。
1 | import * as React from 'react'; |
Children
总所周知,React的children可以为很多种数据类型,一个个声明的话绝对让人崩溃,还好flow提供了一个简单的方式。
1 | import * as React from 'react'; |
你可能注意到了上面使用了import * as React from 'react';
引入React,而不是import React from 'react
。通过查看flow源码可以看到,flow内部已经声明了React的所有类型,使用import * as
的方式可以将类型也一起导出。
React.Node声明:
1 | declare type React$Node = |
特定的Children元素
有时候我们需要组件的children
做限制,比如接受一个函数组件,接受一个特定组件实例,或者多个特定组件。
函数组件:
1 | // @flow |
特定组件实例:
1 | // @flow |
特定的自定义组件
1 | // @flow |
当然children
可能为多个组件,比如选项卡组件,这时需要使用React.ChildrenArray
1 | // @flow |
children
同样可能为函数。
1 | // @flow |
高阶组件
高阶组件(High Order Component)是react中常用的一种模式,它一般来说定义一个函数,接受一个组件以及额外参数,并返回一个新的组件。例如react-redux
的connect
,react-router
中的withRouter
都是高阶组件。
高阶组件的作用在于创建可复用的逻辑,并将其通过props
传给传入的组件。也就是说在调用高阶组件时,可能需要传入额外的props
或者更少的props
(一般来说是更少)。引入flow声明也是如此。
例如,高阶组件会自动传入foo
值
1 | // @flow |
foo
需要声明成可选类型,不然$Diff
会报错。$Diff<{ foo: number }, { foo?: number }>等价于{ foo?: number }
上面我们使用了泛型Props
作为了参数,但是上面提到过定义了defaultProps的参数也是可选的,这样一来Props
就不是完全准确的定义了组件所需的类型。好在Flow提供了React.ElementConfig
帮助我们获取准确的Props
定义。
1 | // @flow |
我们定义了另一个泛型参数Component
,再通过React.ElementConfig<Component>
代替了Props
作为组件的参数。
Context
从React v16.3起新增了React.createContext
api,用于替换以前复杂的context使用方式。
React.createContext
本身是个泛型方法,传入的泛型参数作为value
的类型,如下:
1 | import React from "react"; |
React工具类
React.Node
等价于
1 | // @flow |
React.Element<typeof Component>
声明一个组件的实例元素,也是React.createElement()
方法的返回类型。
1 | // @flow |
1 | // @flow |
React.ChildrenArray<T>
定义特定类型的children
,见上文。
React.ComponentType<Props>
定义特定Props
的组件,包括函数组件和类组件。
React.StatelessFunctionalComponent<Props>
同上,不过特定无状态的函数组件。
React.ElementType
与React.ComponentType<Props>
类似,不同之处在于它支持jsx的内联元素,如div
。
它的声明大致为
1 | declare type React$ElementType = |
React.Key
用于声明react的key值类型
1 | type Key = string | number; |
React.Ref<typeof Component>
用于声明Ref,支持V16的新的api与旧的语法
1 | type React$Ref<ElementType: React$ElementType> = |
React.ElementProps<typeof Component>
用于获取一个组件的的Props
类型。
React.ElementConfig<typeof Component>
同上,不同之处在于会处理defaultProps属性为可选参数。
React.ElementRef<typeof Component>
用于获取一个组件的实例类型。
类组件会返回类的实例
函数组件会返回undefined
jsx内联属性会返回DOM实例,例如
React.ElementRef<'div'>
会返回HTMLDivElement
注意和React.Element<typeof Component>
不同。一个返回React元素,一个返回组件的实例。
热评话题