前言:在单纯的 React & redux 的开发中,我们可以使用 redux-amrc
来帮助我们进行自动异步流程管理,然而在引入了 immutable 不可变数据结构以后。由于这个库只支持原始 JS 数据类型,故而无法使用,下面我们尝试着自己封装一个适用于 immutable 结构的 amrc 库出来
回顾一下 redux-amrc 的使用方法
这里只演示 fetch 操作的部分,具体的集成方法请详见官方网站,官方地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| import { ASYNC } from 'redux-amrc';
function loadData() { return { [ASYNC]: { key: 'data', promise: () => fetch('/api/data') .then(response => response.json()), once: true } }; }
|
编写支持 immutable 数据结构的 redux-immutable-amrc
下面我们编写支持 immutable 数据结构的 ajax 自动流程管理库,需要达到的目标有:
- 支持 immutable 数据类型
- 不需要再手动编写异步 action 对象。
- 不需要再手动编写 reducer 来处理异步 action 对象。
- 获取插件自动生成的 value、error、loaded、loading、loadingNumber 等多个异步状态。
以上最重要的就是支持 immutable 数据类型,这里我们选用的 immutable 库是 facebook 推出的 ( 本文使用的版本是 3.8.2, 4.0 目前还是 rc 版,不推荐使用 ),官方网站
Step 1: 编写 actions
actions/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| export const LOAD = '@async/LOAD'; export const LOAD_SUCCESS = '@async/LOAD_SUCCESS'; export const LOAD_FAIL = '@async/LOAD_FAIL';
export function load(key) { return {type: actions.LOAD, payload: {key: key}}; } export function loadSuccess(key, data) { return {type: actions.LOAD_SUCCESS, payload: {key: key, data: data}}; } export function loadFail(key, error) { return {type: actions.LOAD_FAIL, payload: {key: key, error: error}}; }
|
Strp 2: 编写 reducer
reducer/ajaxReducer.js
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 49 50 51 52
| import Immutable from 'immutable';
import { LOAD, LOAD_SUCCESS, LOAD_FAIL } from '../../actions';
let initialState = Immutable.fromJS({ loadingNumber: 0, loadState: {} });
export function ajaxManager(state = initialState, actions) { let action = actions; switch (action.type) { case LOAD: return state .update('loadingNumber', x => x + 1) .setIn(['loadState', action.payload.key], Immutable.fromJS({ loading: true, loaded: false })); case LOAD_SUCCESS: return state .update('loadingNumber', x => x - 1) .setIn(['loadState', action.payload.key], Immutable.fromJS({ loading: false, loaded: true, error: null })) .set(action.payload.key, Immutable.fromJS(action.payload.data)); case LOAD_FAIL: return state .update('loadingNumber', x => x - 1) .setIn(['loadState', action.payload.key], Immutable.fromJS({ loading: false, loaded: false, error: action.payload.error })) default: return state; } }
|
Step 3: 编写 createReducer
reducer/ajaxReducer.js
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| exports.createReducer = function (reducers) { let reducerKeys = Object.keys(reducers || {}); return function (state, action) { let asyncState = ajaxManager(state, action); let otherState = Immutable.Map(); let otherValue = null; for (let i = 0; i < reducerKeys.length; i += 1) { let key = reducerKeys[i]; if (typeof reducers[key] === 'function') { otherValue = reducers[key](asyncState.get(key), action); otherState = otherState.set(key, otherValue); } } return Immutable.Map().merge(asyncState, otherState); }; };
|
上面的 createReducer 用于操作 ajax 拿到的数据,可以在 ajaxManager 这个大的 reducer 中,再放入自定义的 reducer。把 ajaxManager 的结果和自定义 reducer 的结果混合起来,塞入 store 中。
上面的代码中,由于 Immutable 使用的 3.8.2 版本并没有实现 Immutable.merge 类方法,故而使用 Immutable.Map().merge
折中替代
Step 4: 封装 fetch 操作,自动管理 ajax 流程
fetch/index.js
1 2 3 4 5 6 7 8 9 10 11
| export function fetchAjax(parameter = {}) { return (dispatch, getState) => { dispatch(load(parameter.key)); let p = parameter.promise(parameter.key); return p.then(data => dispatch(loadSuccess(parameter.key, data))) .catch(error => dispatch(loadFail(parameter.key, error.toString()))); } }
|
上面的例子,同时借助了 redux-thunk 中间件,来帮助我们 dispatch actions。
上面返回的是一个高阶函数 HOC,这样做的目的是为了简化我们实际发起 ajax 的操作
整体流程图
使用方法
下面我们来看一下上面自己编写的 redux-immutable-armc 的使用方法
集成方法
reducers/index.js
1 2 3 4 5 6 7 8 9 10 11 12 13
| import {combineReducers} from 'redux-immutable '; import { createReducer } from './reducer/ajaxReducer.js '; import { myReducer } from './reducer/myReducer.js ';
const rootReducer = combineReducers({ async: createReducer({ MemberInfo: myReducer }) });
export default rootReducer;
|
使用举例
1 2 3 4 5 6 7
| export function fetch_membership() { return fetchAjax({ key: 'MemberInfo', promise: () => fetch({api: 'http://127.0.0.1:3500/MemberInfo'}) .then(res => res.json()) }); }
|
上面的例子中,我们发起了一个 fetch 请求,api 地址是本地 3500 端口下的 MemberInfo api。
请求成功后,redux-immutable-amrc 会在 store 下建立一个 async 的 key。再其下面,例子中的 key 会成为拿到的 ajax 数据的 key,获取的数据将作为值存放在这个 key 下。结果如下所示:
1 2 3 4 5 6 7 8 9 10 11 12 13
| { async: { loadingNumber: 0, MemberInfo: true, loadState: { MemberInfo: { loading: false, loaded: true, error: null } }, } }
|
fetch 到的 ajax 数据存放在 store async 下字段名为 MemberInfo ( 制定的 key ) 下,同时 loadState 管理着该 ajax 的请求状态,loadingNumber 记录着该 react 应用中目前所拥有的所有正在进行中的 fetch 请求数量