最近向 @types/react 提交了一个变动,改动了 useReducer 的定义,相信各位读者如果要 TypeScript 化,或者已经 TS 化的话,有可能会收到影响。

通过安装 @types/react@16.9.17 可以使用新的类型定义,这里简单的介绍一下这是怎样的一个能力。

我们首先看一下 react 官方 useReducer 文档中的 case,参见:

const initialState = {count: 0};function reducer(state, action) {switch (action.type) {case 'increment':return {count: state.count + 1};case 'decrement':return {count: state.count - 1};default:throw new Error();}
}function Counter() {const [state, dispatch] = useReducer(reducer, initialState);return (<>Count: {state.count}<button onClick={() => dispatch({type: 'decrement'})}>-</button><button onClick={() => dispatch({type: 'increment'})}>+</button></>);
}

@types/react 对这种 case 的支持是很充分的。然而,我们可能会写一个不需要 action 的 reducer,这种情况下,其实可以认为这个 reducer 只接受唯一可能的 action,如:

const [count, increase] = useReducer(v => v + 1, 0);

有些人会说,为什么不用 useState 来开发这种需求,如果用 useState:

const [count, setCount] = useState(0);
const increase = setCount(v => v + 1);

然而,increase 会在每次 render 的时候进行初始化,意味着每一次它是不一样的,如果不希望对子组件产生影响,就需要写成:

const [count, setCount] = useState(0);
const increase = useCallback(() => setCount(v => v + 1),[]
);

这样的代码我是不太能忍受的,react 在内部用类似 reducer = (state, action) => action 的方式实现 setState,结果开发者又要用 setState 重新实现 reducer。。。体会一下 code smell。。。

然而,@types/react 并没有考虑这个情况,这样的写法会得到一个 TS 报错:

const [count, increase] = useReducer(v => v + 1, 0);// TS2554: Expected 1 arguments, but got 0.
increase();

在考虑 reducer 永远接受 action 的情况下,这是正确的:

type Reducer<S, A> = (state: S, action: A) => S;type Dispatch<A> = (action: A) => void;// 伪代码,这里有 R 满足 R extends Reducer<S, A>
type UseReducer<R, S, A> = (reducer: R, initialArg: S) => [S, Dispatch<A>];

Dispatch<A> 会检查 dispatch(action) 中 action 的类型永远为 Reducer<S, A> 中的 A。
这里涉及到 TS 里的 类型推导功能,当我写:

function identity<T>(arg: T): T {return arg;
}const result = identity('myString');

TS 会识别 'myString' 的 类型为 string,并且推断出 result 的类型也为 string。

所以,回到上面,当我们 dispatch 传入空值的时候,TS 会报错并期望一个符合 action: A 的值。

然而,我们不能简单的通过 (action?: A) => void 来解决这个问题,当 Reducer 接受 action 参数的时候,我们希望 dispatch 一个确实符合的 action。而当 Reducer 不接受 action 参数的时候,我们才希望 dispatch 可以不传入一个 action,即:

const [, dispatch] = useReducer((sum, n) => sum + n);// Pass
dispatch(1);// ExpectError
dispatch();const [, dispatchWithoutAction] = useReducer(sum => sum + 1);// Pass
dispatchWithoutAction();

我们要使用 TS 的另一个特性 overload(重载)来实现这个需求,可以简化为:

type FuncBlank = () => void;
type Func<T> = (value: T) => void;function createFunc(): FuncBlank;
function createFunc<T>(value: T): Func<T>;
function createFunc<T>(value?: T): FuncBlank | Func<T> {// return ...
}

这样,调用 createFunc() 会得到一个 FuncBlank,调用 createFunc('myString') 会得到一个 Func<string>

在 useReducer 的重载上也是一样,我们需要判断 reducer 是一个带 action 的 reducer,或者是一个不带 action 的 reducer,来决定我们返回的 dispatch 的类型是否接受参数。

具体的实现可以参见 PR

大概就是这样,在此非常感谢 @LeuisKen @Gavin @NullMDR 的帮助。

react dispatch_记 react 项目在 TypeScript 化中的一个坑,以及相应的类型改动相关推荐

  1. 记前端状态管理库Akita中的一个坑

    记状态管理库Akita中的一个坑 Akita是什么 Akita是一种基于RxJS的状态管理模式,它采用Flux中的多个数据存储和Redux中的不可变更新的思想,以及流数据的概念,来创建可观察的数据存储 ...

  2. idea 启动php项目路径,关于idea中Java Web项目的访问路径问题

    说明 这里只以 servlet 为例,没有涉及到框架,但其实路径的基本原理和框架的关系不大,所以学了框架的同学如果对路径有疑惑的也可以阅读此文 项目结构 在 idea 中新建一个 Java Web 项 ...

  3. React.js 小书 Lesson1-2 - 前端组件化(一):从一个简单的例子讲起

    React.js 小书 Lesson1-2 - 前端组件化(一):从一个简单的例子讲起 本文作者:胡子大哈 本文原文:http://huziketang.com/books/react/lesson2 ...

  4. React 折腾记 - (1) React Router V4 和antd侧边栏的正确关联及动态title的实现

    前言 一如既往,实战出真理. 有兴趣的可以瞧瞧,没兴趣的大佬请止步于此. 免得浪费您的时间 效果图 基于antd的sidebar组件封装 折腾记的技术栈选型 Mobx & mobx-react ...

  5. 谈谈我对前端组件化中“组件”的理解,顺带写个Vue与React的demo

    谈谈我对前端组件化中"组件"的理解,顺带写个Vue与React的demo 前言 前端已经过了单兵作战的时代了,现在一个稍微复杂一点的项目都需要几个人协同开发,一个战略级别的APP的 ...

  6. React——脚手架创建、基本操作、Typescript

    React官方文档: https://react.docschina.org/ 1. 全局安装 cnpm i -g create-react-app 2. 创建项目 /*js*/ create-rea ...

  7. 使用react实现后台管理系统项目

    一.开发React必须依赖三个库 1.react:包含react所必须的核心代码 2.react-dom:react渲染在不同平台所需要的核心代码 3.babel:将jsx转换成React代码的工具 ...

  8. [react] 你有在项目中使用过Yeoman脚手架吗?

    [react] 你有在项目中使用过Yeoman脚手架吗? 前端工程化脚手架部分必备的依赖,用于初始化生成规范的项目目录 个人简介 我是歌谣,欢迎和大家一起交流前后端知识.放弃很容易, 但坚持一定很酷. ...

  9. fs react 使用 保存文件_入门TypeScript编写React

    使用 create-react-app 开启 TypeScript Create React App 是一个官方支持的创建 React 单页应用程序的CLI,它提供了一个零配置的现代构建设置.当你使用 ...

最新文章

  1. SmartGit安装及使用
  2. [其他]JAVA与C#的Socket通信
  3. 第17节 业务流程管理和重组
  4. 用js自动把url加入ubb代码的函数
  5. Android之编译jni出错解决办法
  6. Matrix Computations 1
  7. Expected one result (or null) to be returned by selectOne(), but found: 2
  8. 基于JAVA+SpringMVC+Mybatis+MYSQL的学生健康信息管理系统
  9. Gcc 完全参考手册,参数说明,操作指南-Gcc Complete referene
  10. iphone开发我的新浪微博客户端-用户登录账号删除篇(1.6)
  11. Linux基础命令---accept打印机控制
  12. spring boot RESTFul API拦截 以及Filter和interceptor 、Aspect区别
  13. php 支付宝支付回调与查询订单
  14. windows server2003的邮箱服务器安装详细步骤
  15. 选择目录或选择文件(PyQt或Qt for python)
  16. 【Android】移动端接入Cronet实践
  17. CFS任务放置代码详解
  18. Android 侧边栏快速索引(点击索引、滑动索引),通讯录样式
  19. 小米笔记本锁屏睡眠无法唤醒修复方法
  20. E22 LoRa模块透传 定点传输 WOR模式测试与MicroPython应用

热门文章

  1. php与mysql关系大揭秘_【慕课笔记】PHP与MySQL关系大揭秘
  2. 如何安装html网站模板,网站模板安装说明
  3. linux mint 设置分辨率,Vmware中装Linux Mint 15 添加 1600x900分辨率
  4. 转载:(C++)浅谈多态基类析构函数声明为虚函数
  5. 关于SpringMVC中text/plain的编码导致的乱码问题解决方法
  6. html中hr的各种样式使用
  7. 覆写Activity的finish()方法
  8. 1.4 Arithmetic Progressions
  9. GridView控件日期格式化
  10. ‘cross-env‘ 不是内部或外部命令,也不是可运行的程序 或批处理文件。