Reactjs+Flow:SyntheticKeyboardEvent与Props传递函数的技术问询
一、SyntheticKeyboardEvent 的 Flow 类型处理
在React里处理键盘事件时,Flow需要明确事件的类型才能做准确的类型检查。咱们可以直接用React.SyntheticKeyboardEvent来定义事件参数的类型,还可以指定触发事件的DOM元素类型,让Flow能正确推断元素的属性。
比如你要给一个输入框加回车触发搜索的逻辑,可以这么写:
handleKeyPress = (e: React.SyntheticKeyboardEvent<HTMLInputElement>) => { if (e.key === 'Enter') { e.preventDefault(); // 这里e.currentTarget.value的类型会被Flow正确识别为string this.props.fetchPastLaunchesRequest(e.currentTarget.value); } };
如果你的事件是绑定在按钮或者其他元素上,只需要把<HTMLInputElement>换成对应的类型,比如<HTMLButtonElement>或者通用的<HTMLElement>就行。要是你想解构事件属性,Flow也能完美识别:
handleKeyDown = ({ key, currentTarget }: React.SyntheticKeyboardEvent<HTMLButtonElement>) => { if (key === 'Escape') { currentTarget.blur(); } };
二、函数作为 Props 的 Flow 类型定义
看你代码里已经定义了Props类型,其中fetchPastLaunchesRequest是(launch_year: string) => Function,其实咱们可以把这个类型定义得更精确,这样Flow能帮你检查参数和返回值的类型是否正确。
1. 直接在Props里定义函数签名
如果你的action是redux-thunk风格的(返回一个函数),可以这么写:
// 先导入redux的Dispatch类型 import type { Dispatch } from 'redux'; type Props = { // 明确接受string类型的年份参数,返回一个接收dispatch的函数 fetchPastLaunchesRequest: (launchYear: string) => (dispatch: Dispatch) => Promise<void>, launches: Array<Object>, hasErrored: boolean, isLoading: boolean };
要是不想太细致,也可以用Function,但具体的签名能让Flow提前帮你发现参数类型不匹配的问题。
2. 单独定义函数类型提升可读性
如果这个函数会在多个地方用到,单独抽离类型会让代码更清晰:
import type { Dispatch } from 'redux'; type FetchPastLaunches = (launchYear: string) => (dispatch: Dispatch) => Promise<void>; type Props = { fetchPastLaunchesRequest: FetchPastLaunches, launches: Array<Object>, hasErrored: boolean, isLoading: boolean };
3. 传递函数给子组件时的类型检查
当你把函数传给子组件(比如你的Picker组件),子组件的Props也要正确定义函数类型,这样Flow会检查父组件传递的函数是否符合要求:
// Picker组件的Flow类型定义 type PickerProps = { onYearSelect: (year: string) => void, // 其他Props... }; const Picker = ({ onYearSelect }: PickerProps) => ( <select onChange={(e) => onYearSelect(e.target.value)}> {/* 年份选项 */} </select> );
这样当你在AsyncApp里把fetchPastLaunchesRequest传给Picker的onYearSelect时,Flow会自动检查参数类型是否匹配,避免你不小心传错参数。
另外,如果你用类组件的方法作为Props传递,记得绑定this(用箭头函数或者构造函数绑定都可以),Flow也能正确识别绑定后的方法类型:
// 用箭头函数避免this绑定问题 <Picker onYearSelect={(year) => this.props.fetchPastLaunchesRequest(year)} />
三、结合你的 AsyncApp 组件示例
给你的AsyncApp组件加上键盘事件处理和正确的类型定义后,代码大概是这样的:
// @flow import React from "react"; import { connect } from "react-redux"; import _ from "lodash"; import { fetchPastLaunchesRequest } from "../actions/launches"; import { LaunchList } from "../components/launches/LaunchList"; import { Picker } from "../components/Picker"; import type { Dispatch } from 'redux'; type FetchPastLaunches = (launch_year: string) => (dispatch: Dispatch) => Promise<void>; type Props = { fetchPastLaunchesRequest: FetchPastLaunches, launches: Array<Object>, hasErrored: boolean, isLoading: boolean }; class AsyncApp extends React.Component<Props> { handleSearchKeyPress = (e: React.SyntheticKeyboardEvent<HTMLInputElement>) => { if (e.key === 'Enter') { e.preventDefault(); this.props.fetchPastLaunchesRequest(e.currentTarget.value); } }; render() { const { launches, hasErrored, isLoading } = this.props; return ( <div> <input type="text" placeholder="输入年份按回车搜索" onKeyPress={this.handleSearchKeyPress} /> <Picker onYearSelect={this.props.fetchPastLaunchesRequest} /> {isLoading && <p>加载中...</p>} {hasErrored && <p>加载失败!</p>} <LaunchList launches={launches} /> </div> ); } } export default connect( (state) => ({ launches: state.launches.past, hasErrored: state.launches.hasErrored, isLoading: state.launches.isLoading }), { fetchPastLaunchesRequest } )(AsyncApp);
这样所有的类型都会被Flow检查,能有效避免类型错误。
内容的提问来源于stack exchange,提问作者jwesonga

