react redux异步处理的中间件

搭建基本环境

首先我有一个处理逻辑的container

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
import * as React from "react";
import { getMusicLists } from "../../store/actions";
import { connect } from "react-redux";

interface IMusic {
name: string;
}
interface IProps {
musicLists: IMusic[];
getMusicLists: () => void;
}

class MusicContainer extends React.Component<IProps> {
public onClickBtn = (): void => {
this.props.getMusicLists();
};
public render(): React.ReactElement {
const { musicLists } = this.props;
return (
<div>
<h5>音乐列表逻辑处理页面</h5>
<button onClick={this.onClickBtn}>获取数据</button>
<div>
{musicLists.length > 0
? musicLists.map((list: IMusic) => {
return <div key={list.name}>{list.name}</div>;
})
: "暂无数据"}
</div>
</div>
);
}
}

const mapStateToProps = (state: any) => ({
musicLists: state.musicLists
});

const mapDispathToProps = (dispatch: any) => ({
getMusicLists: () => {
dispatch(getMusicLists());
}
});

export default connect(
mapStateToProps,
mapDispathToProps
)(MusicContainer);

然后建一个store文件夹。文件夹下的文件为:

  • action-types.ts
  • actions.ts
  • reducer.ts
  • store.ts

action-types

1
2
export const GET_MUSIC_LISTS = 'GET_MUSIC_LISTS';
export const UPDATE_MUSIC_LISTS = 'UPDATE_MUSIC_LISTS';

actions

1
2
3
4
5
import { GET_MUSIC_LISTS } from "./action-types";

export const getMusicLists = () => ({
type: GET_MUSIC_LISTS,
})

reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
import { GET_MUSIC_LISTS, UPDATE_MUSIC_LISTS } from "./action-types";

const initState = {
musicLists: []
};

const reducer = (state = initState, action: any) => {
const { type } = action;
switch (type) {
default:
return state;
}
};

export default reducer;

store

1
2
3
4
5
import reducer from './reducer';
import {createStore} from 'redux';

const store = createStore(reducer);
export default store;

下面我们分别用redux-thunkredux-saga来处理异步操作,看看这两者的使用区别。

redux-thunk

安装

1
npm install redux-thunk --save

store

1
2
3
4
5
6
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import thunk from "redux-thunk";

const store = createStore(reducer, applyMiddleware(thunk));
export default store;

actions

1
2
3
4
5
6
7
8
9
10
11
12
13
import { GET_MUSIC_LISTS, UPDATE_MUSIC_LISTS } from "./action-types";

export const getMusicLists = () => (dispatch: any) => {
setTimeout(() => {
const res = [{ name: "123" }];
dispatch({
type: UPDATE_MUSIC_LISTS,
payload: {
musicLists: res
}
});
}, 1000);
};

这里是redux-thunk使用的关键地方。我们普通的action是直接返回一个对象,但当我们使用了thunk中间件之后,我们可以直接在action中返回一个函数。thunk中间件会判断我们的action返回的是函数还是一个对象,是函数的话,它便会拦截后做相应的处理。

reducer

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
import { GET_MUSIC_LISTS, UPDATE_MUSIC_LISTS } from "./action-types";

const initState = {
musicLists: []
};

const reducer = (state = initState, action: any) => {
const { type } = action;
switch (type) {
case UPDATE_MUSIC_LISTS:
return {
...state,
musicLists: action.payload.musicLists
}
default:
return state;
}
};

export default reducer;

我们理一下这里的逻辑。首先我们点击后派发了getMusicLists这个action,我们在这个action中进行了一些异步的处理,这个异步处理后,我们再dispatch了一个用于更新数据的plain object的action,以此来更新数据。

redux-saga

当我们使用redux-thunk来处理异步数据更新时,会发现我们的action的逻辑会逐渐变得复杂,然而理论上action应该是要尽可能简洁,返回一个plain object。redux-saga便可以处理这种问题。它就相当于把刚才我们写在action里的逻辑提取出来,单独再写一个专门处理这些逻辑的文件。

接下来我们使用redux-saga来重写一遍刚才的逻辑。首先还原到使用redux-thunk之前。

安装

1
npm install redux-saga --save

首先action types我们是不用改的。主要的改动是以下

1、actions还原,使其只返回plain object

2、新建saga文件,专门用来处理副作用,也就是处理一些异步请求等操作

3、引用redux-saga这个中间件

actions

1
2
3
4
5
import { GET_MUSIC_LISTS } from "./action-types";

export const getMusicLists = () => ({
type: GET_MUSIC_LISTS
});

saga

新建saga文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
import {takeLatest, put, delay} from 'redux-saga/effects'
import { GET_MUSIC_LISTS, UPDATE_MUSIC_LISTS } from './action-types';

function* getMusicListsSaga() {
yield delay(1000);
const res = [{ name: "123" }];
yield put({
type: UPDATE_MUSIC_LISTS,
payload: {
musicLists: res
}
});
}
export default function* rootSaga() {
yield takeLatest(GET_MUSIC_LISTS, getMusicListsSaga)
}

store

引用saga中间件,因为我是专门写了一个store文件,因此改这个文件内容

1
2
3
4
5
6
7
8
9
10
import reducer from "./reducer";
import { createStore, applyMiddleware } from "redux";
import createSagaMiddleware from "redux-saga";
import rootSaga from "./saga";

const sagaMiddleware = createSagaMiddleware();

const store = createStore(reducer, applyMiddleware(sagaMiddleware));
sagaMiddleware.run(rootSaga);
export default store;

整体流程基本完了。可以发现,我们并没有改过musiccontainer里的逻辑,所以redux-thunk转移至redux-saga也不会太难。不过逻辑复杂的情况没有尝试过,不敢说的太绝对。