by Fabian Terh

由Fabian Terh

使用Flow检查React,Redux和React-Redux的全面指南 (A comprehensive guide to type checking React, Redux, and React-Redux with Flow)

This article is divided into 4 sections:

本文分为4个部分:

  1. Type checking Redux actions, action creators, and reducers类型检查Redux操作,操作创建者和简化器
  2. Installing Flow library definitions安装流库定义
  3. Type checking application state类型检查应用程序状态
  4. Type checking Redux store and dispatch类型检查Redux存储和调度

While there are a bunch of guides on the first section which are immensely helpful, I found only a paucity of articles on 3 and 4. After a long session of Google searches, diving into source code, and trial and error, I decided to put together what I’ve learned and write this tutorial as a one-stop guide to type checking your React + Redux + React-Redux application with Flow.

尽管在第一部分中有很多指南对您有很大帮助,但是我发现关于3和4的文章很少。经过一番Google搜索,深入研究源代码以及反复试验之后,我决定放下结合我所学到的知识,并将本教程作为一站式指南来编写,以使用Flow对您的React + Redux + React-Redux应用程序进行类型检查。

1.类型检查Redux操作,操作创建者和简化器 (1. Type checking Redux actions, action creators, and reducers)

动作 (Actions)

Redux actions are essentially vanilla Javascript objects with a mandatory type property:

Redux操作本质上是具有强制type属性的原始Javascript对象:

// This is an action{  type: 'INCREASE_COUNTER',  increment: 1}

Following best practices, you may want to define and use action type constants instead. If so, the above snippet would probably look something like this:

按照最佳实践,您可能想要定义和使用操作类型常量 。 如果是这样,上面的代码段可能看起来像这样:

const INCREASE_COUNTER = 'INCREASE_COUNTER';
// This is an action{  type: INCREASE_COUNTER,  increment: 1}

Type checking is easy (we’re dealing with regular JavaScript here):

类型检查很容易(我们在这里处理常规JavaScript):

type $action = {  type: 'INCREASE_COUNTER',  increment: number};

Note that you cannot substitute the literal string type with the constant INCREASE_COUNTER. This is a limitation of Flow itself.

注意,不能用常量INCREASE_COUNTER代替文字字符串类型 。 这是Flow本身的局限性 。

动作创作者 (Action creators)

Since action creators are just functions that return actions, we’re still dealing with regular Javascript. This is how a type checked action creator can look like:

由于动作创建者只是返回动作的函数,因此我们仍在处理常规Javascript。 这是类型检查动作创建者的样子:

function increaseCounter(by: number): $action {  return {    type: INCREASE_COUNTER, // it's okay to use the constant here    increment: by  };}

减速器 (Reducers)

Reducers are functions that handle actions. They receive a state and an action, and return the new state. At this juncture, it’s important to think about how your state will look like (state shape). In this very simple example, the state shape comprises of only a single key counter which takes on a number type value:

减速器是处理动作的功能。 他们收到一个状态和一个动作,并返回新状态。 在此关头,考虑一下您的状态(状态形状)是很重要的。 在这个非常简单的示例中,状态形状仅包含一个带有number类型值的键counter

// State shape{  counter: <number>}

And so your reducer could look like this:

因此,您的减速器可能如下所示:

const initialState = { counter: 0 };
function counter(state = initialState, action) {  switch (action.type) {    case INCREASE_COUNTER:      return Object.assign({}, state, {        counter: action.increment + state.counter      });        default:      return state;  }};

Note: In this particular example, Object.assign({}, state, { ... }) is redundant because the store consists only of 1 key/value pair. I could just as easily return the last argument to the function. However, I included the full implementation for correctness.

注意:在此特定示例中, Object.assign({}, state, { ... }) 是多余的,因为存储仅由1个键/值对组成。 我可以轻松地将最后一个参数返回给函数。 但是,为了正确起见,我包含了完整的实现。

Typing the state and reducer is simple enough. Here is the typed version of the above snippet:

输入状态和reducer很简单。 这是类型 以上代码段的版本:

type $state = {  +counter: number};
const initialState: $state = { counter: 0 };
function counter(  state: $state = initialState,  action: $action): $state {    switch (action.type) {    case INCREASE_COUNTER:      return Object.assign({}, state, {        counter: action.increment + state.counter      });        default:      return state;  }};

安装流库定义 (Installing Flow library definitions)

Flow library definitions (or libdefs) provide type definitions for third-party modules. In this case, we are using React, Redux, and React-Redux. Instead of typing these modules and their functions manually, you can install their type definitions using flow-typed:

流库定义 (或libdefs)为第三方模块提供类型定义。 在这种情况下,我们使用React,Redux和React-Redux。 您可以使用flow-typed安装它们的类型定义,而不必手动键入这些模块及其功能:

npm install -g flow-typed
// Automatically download and install all relevant libdefsflow-typed install
// Orflow-typed install <package>@<version> // e.g. redux@4.0.0

Library definitions are installed to the flow-typed folder, which lets Flow work out of the box without any further configuration (details).

库定义将安装到flow-typed文件夹中,这使Flow可以直接使用,而无需任何进一步的配置( 详细信息 )。

类型检查应用程序状态 (Type checking application state)

Previously, we have already typed the state like this:

以前,我们已经输入了如下状态:

type $state = {  +counter: number};

While this works for a simple example like the one above, it breaks down once your state becomes significantly larger. You would have to manually edit type $state every time you introduce a new reducer or modify an existing one. You also wouldn’t want to keep all your reducers in the same file. What you want to do instead is to refactor your reducers into separate modules, and use Redux’s combineReducers function.

尽管这对于上面的示例很简单,但是一旦您的状态显着变大,它就会崩溃。 每次引入新的reducer或修改现有的reducer时,都必须手动编辑type $state 。 您也不想将所有的reducer放在同一文件中。 相反,您要做的是将减速器重构为单独的模块,并使用Redux的combineReducers函数。

Since the focus of this article is on type checking a React/Redux/React-Redux application, and not on building one, I’m going to assume you are familiar with the combineReducers function. If not, head over to Redux’s tutorial to learn all about it.

由于本文的重点是对React / Redux / React-Redux应用程序进行类型检查 ,而不是构建应用程序,因此,我假定您熟悉combineReducers函数。 如果没有,请转到Redux的教程以了解所有内容。

Suppose we introduce a new action/reducer pair in a separate module:

假设我们在单独的模块中引入了一个新的动作/减速器对:

// playSong.js
export const PLAY_SONG = 'PLAY_SONG';
// Typing the actionexport type $playSongAction = {  type: 'PLAY_SONG',  song: ?string};
// Typing the action creatorexport function playSong(song: ?string): $playSongAction {  return {    type: PLAY_SONG,    song: song  };};
// Typing arg1 and the return value of the reducer [*1]export type $song = ?string;
// Typing the state [*1]export type $songState = {  +song: $song};
// [*1][*2]const initialValue: $song = null;
// Typing the reducer [*1][*3]function song(  state: $song = initialValue,  action: $playSongAction): $song {    switch (action.type) {    case PLAY_SONG:      return action.song;        default:      return state;  }};

[*1]: If we’re using the combineReducers function, it’s important to note that your reducer should no longer be returning the state, but rather the value to the key in the state. In this regard, I think Redux’s tutorial is a bit lacking in clarity, as it does not state this explicitly, although it is clear from the example code snippets.

[* 1]:如果我们使用combineReducers函数,则需要注意的是, reduce不再应该返回状态,而应返回state中 。 在这方面,我认为Redux的教程有点不够清晰,因为虽然从示例代码片段中可以清楚地看到,但它没有明确说明。

[*2]: Reducers are not allowed to return undefined, so we have to settle for null.

[* 2]:减速器不允许返回undefined ,因此我们必须满足null

[*3]: Since the reducer is no longer receiving and returning a state in the form of { song: string }, but rather the value to the song key in the state object, we need to change the types of its first argument and return value from $songState to $song.

[* 3]:由于reducer不再以{ song: string }的形式接收和返回状态,而是状态对象中song键的 ,因此我们需要更改其第一个参数的类型,并从$songState$song返回值。

We modify and refactor increaseCounter as well:

我们修改和重构increaseCounter还有:

// increaseCounter.js
export const INCREASE_COUNTER = 'INCREASE_COUNTER';
export type $increaseCounterAction = {  type: 'INCREASE_COUNTER',  increment: number};
export function increaseCounter(by: number): $action {  return {    type: INCREASE_COUNTER,    increment: by  };};
export type $counter = number;
export type $counterState = {  +counter: $counter};
const initialValue: $counter = 0;
function counter(  state: $counter = initialValue,  action: $increaseCounterAction): $counter {    switch (action.type) {    case INCREASE_COUNTER:      return action.increment + state;        default:      return state;  }};

Now we have 2 action/reducer pairs.

现在我们有2个动作/减速器对。

We can create a new State type to store the type of our application state:

我们可以创建一个新的State类型来存储我们的应用程序状态的类型:

export type State = $songState & $counterState;

This is a Flow intersection type, and is equivalent to:

这是流交叉点类型 ,等效于:

export type State = {  song: $song,  counter: $counter};

If you don’t want to create $songState and $counterState only for use in intersection-typing the application state State, that’s perfectly fine too — go with the second implementation.

如果您不想创建$songState$counterState仅用于交叉键入应用程序状态State ,那也很好-与第二个实现一起进行。

类型检查Redux存储和调度 (Type checking Redux store and dispatch)

I found that Flow was reporting errors in my containers (in the context of the container/component paradigm).

我发现Flow正在报告我的容器中的错误(在容器/组件范例的上下文中)。

Could not decide which case to select. Since case 3 [1] may work but if it doesn't case 6 [2] looks promising too. To fix add a type annotation to dispatch [3].

This was with regard to my mapDispatchToProps function. Cases 3 and 6 are as follows:

这与我的mapDispatchToProps函数有关。 情况3和6如下:

// flow-typed/npm/react-redux_v5.x.x.js
// Case 3declare export function connect<  Com: ComponentType<*>,  A,  S: Object,  DP: Object,  SP: Object,  RSP: Object,  RDP: Object,  CP: $Diff<$Diff<ElementConfig<Com>;, RSP>, RDP>  >(  mapStateToProps: MapStateToProps<S, SP, RSP>,  mapDispatchToProps: MapDispatchToProps<A, DP, RDP>): (component: Com) => ComponentType<CP & SP & DP>;
// Case 6declare export function connect<  Com: ComponentType<*>,  S: Object,  SP: Object,  RSP: Object,  MDP: Object,  CP: $Diff<ElementConfig<Com>, RSP>  >(  mapStateToProps: MapStateToProps<S, SP, RSP>,  mapDispatchToPRops: MDP): (component: Com) => ComponentType<$Diff<CP, MDP> & SP>;

I don’t know why this error occurs. But as the error hints, typing dispatch fixes it. And if we’re typing dispatch, we might as well type store too.

我不知道为什么会发生此错误。 但是如错误提示所示,键入dispatch解决该问题。 而且,如果我们输入dispatch ,我们也可以输入store

I couldn’t find much documentation on this aspect of typing a Redux/React-Redux application. I learned by diving into the libdefs and looking at the source code for other projects (albeit a demonstration project). If you have any insights, please let me know so I can update this post (with proper attribution, of course).

在键入Redux / React-Redux应用程序的这一方面,我找不到很多文档。 我通过深入libdefs并查看其他项目 (尽管是演示项目)的源代码来学习 。 如果您有任何见解,请告诉我,以便我可以更新此帖子(当然,请注明出处)。

In the meantime, I’ve found that this works:

同时,我发现这可行:

import type {  Store as ReduxStore,  Dispatch as ReduxDispatch} from 'redux';
// import any other variables and types you may need,// depending on how you organized your file structure.
// Reproduced from earlier onexport type State = {  song: $song,  counter: $counter};
export type Action =   | $playSongAction  | $increaseCounterAction
export type Store = ReduxStore<State, Action>;
export type Dispatch = ReduxDispatch<Action>;

Heading into your container modules, you can then proceed to type mapDispatchToProps as follows: const mapDispatchToProps = (dispatch: Dispatch) => { ... };

进入容器模块,然后可以按如下所示继续输入mapDispatchToPropsconst mapDispatchToProps = (dispatch: Dispatch) => { ... };

结语 (Wrapping up)

This has been a pretty long post, and I hope you found it useful. I wrote it partly because of a dearth of resources regarding the later sections of this article (and partly to organize my thoughts and consolidate what I’ve learned).

这是一篇很长的文章,希望您觉得它有用。 我写这篇文章的部分原因是由于本文后面各部分的资源匮乏(部分原因是为了整理我的思想并巩固我所学的知识)。

I can’t guarantee that the guidance in this article follows best practices, or is conceptually sound, or even 100% correct. If you spot any errors or issues, please let me know!

我不能保证本文中的指导遵循最佳实践,或者从概念上讲是正确的,甚至100%正确。 如果您发现任何错误或问题,请告诉我!

翻译自: https://www.freecodecamp.org/news/a-comprehensive-guide-to-type-checking-react-redux-and-react-redux-with-flow-4219bdc4ef89/

使用Flow检查React,Redux和React-Redux的全面指南相关推荐

  1. 彻底征服 React.js + Flux + Redux【讲师辅导】-曾亮-专题视频课程

    彻底征服 React.js + Flux + Redux[讲师辅导]-74574人已学习 课程介绍         [会员免费]链接 http://edu.csdn.net/lecturer/585 ...

  2. react开发教程(十)redux结合react

    描述 Redux 和 React 之间没有关系.Redux 可以搭配 React.Angular 甚至纯 JS.但是 Redux 还是比较适合和 React 搭配的,因为 React 允许你以 sta ...

  3. React native 项目进阶(redux, redux saga, redux logger)

    之前利用知乎日报的api写了react-native的一个入门项目,传送文章地址React Native 项目入门和源码地址RN入门项目源码,目前github上的代码已经在原文的基础上增加了新的功能, ...

  4. 【React.js 06】Redux基础知识

    Redux是一个专注于状态管理的库,和React是解耦的,用Angular和Vue都可以使用Redux.其具有一个单一状态,单向数据流的特性. Redux概念 redux有一个store,记录所有的s ...

  5. 优雅的在React项目中使用Redux

    概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与React没有任何关系,其他UI框架也可以使用Redux react-redux React插件,作用:方便在 ...

  6. 如何优雅地在React项目中使用Redux

    前言 或许你当前的项目还没有到应用Redux的程度,但提前了解一下也没有坏处,本文不会安利大家使用Redux 概念 首先我们会用到哪些框架和工具呢? React UI框架 Redux 状态管理工具,与 ...

  7. 如何在React Native中使用Redux Saga监视网络更改

    by Pritish Vaidya 通过Pritish Vaidya 如何在React Native中使用Redux Saga监视网络更改 (How to monitor network change ...

  8. Redux 进阶 - react 全家桶学习笔记(二)

    注:这篇是17年1月的文章,搬运自本人 blog... https://github.com/BuptStEve/... 零.前言 在上一篇中介绍了 Redux 的各项基础 api.接着一步一步地介绍 ...

  9. 使用Redux在React Navigation App中管理状态

    by Andrea Bizzotto 通过安德里亚·比佐托(Andrea Bizzotto) 使用Redux在React Navigation App中管理状态 (Managing State in ...

最新文章

  1. visudo使用-怎样将mount权限给普通用户
  2. SQL Server 2000 sp4 简体中文版免费在线下载!
  3. C#如何得到运行中和杀死运行中的进程?
  4. python2.7配置tensorflow1.5.0和keras2.1.6
  5. spring-cloud-eureka服务注册与发现
  6. 【慎思堂】之JS牛腩总结
  7. Python相关的考试和认证
  8. 修改mysql数据引擎的方法- 提高数据库性能
  9. redis中不同value类型的存取操作方式
  10. 几款远程工具介绍(Xshell)(SecureCRT)(putty)
  11. [转载]MySQL各类SQL语句的加锁机制
  12. 资源管理器 右键 反应慢 现象解决方案
  13. 天翼对讲机写频软件_手台对讲机应该怎么选?
  14. 数学建模番外篇2:作图练习-美赛2020E题
  15. linux打印机pdf,在Deepin系统下安装CPUS-PDF虚拟PDF打印机的方法
  16. vue3 + tsx + pinia + jest开发模板整理
  17. linux命令ls查看内存,Linux ls 命令详解
  18. 批量转账到支付宝ISV(API接口流程步骤)
  19. java 中 public,default,protected,private区别
  20. 基于单例模式的多键值序列号生成器实现(支持缓存)

热门文章

  1. Vmware Workstation VMX 在资源管理器中杀不掉(虚拟机繁忙导致无法关机)
  2. CSS之布局(文档流)
  3. uniapp富文本复制文字内容
  4. AE 动画直接变原生代码:Airbnb 发布开源动画库 Lottie
  5. 浅谈网络协议(四) IP的由来--DHCP与PXE
  6. DIY 一套正版、免费、强大的 Visual Studio 2012 IDE
  7. 使用Silverlight2的WebClient下载远程图片
  8. jenkins添加git源码目录时报Error performing command错误
  9. 《Linux内核设计与实现》读书笔记 第三章 进程管理
  10. 高性能ORM框架XLinq功能详细介绍