Redux、Rxjs结合TypeScript在react中的简单使用

1,117 阅读3分钟

Redux和TypeScript参考官网即可。Rxjs作为一个反应式编程思想的JS实现库,在前端这种多交互、多请求的环境下,很适合使用。同时,Redex与Rxjs的结合redux-observable,也能解决异步Action的问题。个人认为,Rxjs在解决复杂交互、数据流处理这些方面都是非常合适的,当然,因为学习曲线相对比较复杂、且需要转变思想,运用的比较少。Rxjs的介绍与使用在本篇就不介绍了,本文重点介绍这几个库在React中的结合使用,以及一些注意点。

本文以redux-observable官方的一个示例为基础

一、准备

1、 相关库:

2、相关目录结构:

-src
  -store
    -epics
        counter.ts // 存放所有counter相关的epic
        index.ts   // 收集所有epic并合并,对外暴露
    -actions
        index.ts // 收集所有action并合并,对外暴露
    -reducers
        counter.ts // 存放所有counter相关的reducers
        index.ts // 收集所有reducer并合并,对外暴露
    index.ts // 处理所有redux相关,并对外暴露
    types.ts // 定义store相关type

二、定义相关类型

// types.ts

import { ActionType } from 'typesafe-actions';
import * as actions from 'store/actions';

export interface RootState { // 定义根数据类型
  counter: number;
}

export const INCREMENT = 'INCREMENT'; // 定义一个Reducre的Type
export const INCREMENT_IF_ODD = 'INCREMENT_IF_ODD'; // 定义一个Reducre的Type

export type Action = ActionType<typeof actions>; // 定义所有Action的类型、使用ActionType从actions里面获取

三、定义Action

因为我们在types.ts先把所有Action定义了,所以我们可以先定义action.

import { action, createAction } from 'typesafe-actions';
import { INCREMENT, INCREMENT_IF_ODD } from '../types';
export const incrementCounter = () => action(INCREMENT);
export const incrementOddCounter = createAction(INCREMENT_IF_ODD)();

createAction与action作用是一致的,createAction功能上更多一点,具体可以官网查看。

四、定义Reducer

和常规的Reducer没什么区别,当然可以使用typesafe-actions的createReducer创建更复杂、功能更全的。

// counter.ts
import {
  INCREMENT,
  Action,
} from '../types';

const initState: number = 0;
const counter = (state = initState, action: Action): number => {
  switch (action.type) {
    case INCREMENT:
      return state + 1;
    default:
      return state;
  }
};
export default counter;

需要注意的是,当action中带入数据传入的时,action中的数据会被redux-observable包装了一层,在action.payload下,这点取值时需要注意。

五、定义Epic

import { map, filter } from 'rxjs/operators';
import { ofType } from 'redux-observable';
import { Epic } from 'redux-observable';
import { incrementCounter, incrementOddCounter } from 'store/actions';

import { RootState } from '../types';

import { Action, INCREMENT_IF_ODD } from '../types';
console.log(incrementOddCounter);

const incrementIfOddEpic: Epic<Action, Action, RootState> = (action$, state$) => action$.pipe(
  ofType(INCREMENT_IF_ODD), // 所有的Action都会触发Epic,为了获取指定的,我们需要先过滤。
  filter(() => state$.value.counter % 2 === 1), // 过滤掉使counter不为奇数的INCREMENT_IF_ODD,
  map(() => incrementCounter()) // 最终映射到INCREMENT的action。
);
export default [incrementIfOddEpic];

六、结尾

reducer、action、reducer都创建完成之后就是一些连接的工作,基本无异。本文仅是简单的一个介绍,上面的epic中,我们未使用异步操作,我们可以在ofType之后执行异步操作,如:

pipe(
    ofType(FETCH_REMOTE),
    switchMap(action => from(fetch('/api/user')).pipe(
        map(getUser) // getUser是action
    ))
)

通过switchMap把数据流最终写入到getUser相关的state。这样的流操作是不是方便很多。Rxjs还有更多的操作。 自己写的一个简单示例已经上传到github,同时还有一个很好的参考项目提供给大家。