前言
公司中台项目使用 redux 进行状态管理,众所周知,redux 写起来就是在浪费生命。但鉴于项目重构成本太大,因此决定先在不改变框架的情况下,在原先代码的基础上一步步优化简化 redux 用法。
优化
先来看看原来的文件目录结构:
├── store
│ └── modules
│ ├── todoList
│ │ ├── todoList_action.js
│ │ ├── todoList_reducer.js
│ │ ├── todoList_state.js
│ │ └── todoList_types.js
页面代码:
// todoList_types.js
export const types = {
TODO_LIST: "TODO_LIST",
};
// todoList_action.js
import types from "./todoList_types";
// 接口
import { todoListSvr } from "services/card";
// 使用了 thunkMiddleware 中间件
export const todoList = (params) => ({
type: types.TODO_LIST,
payload: {
// 使用了 promiseMiddleware 中间件
promise: todoListSvr(params),
},
});
// todoList_reducer.js
import types from "./todoList_types";
import initialState from "./todoList_state";
// 工具函数,用于简化 reducer 代码
export const createReducer = (initialparams, reducerMap) => (
params = initialparams,
action
) => {
const reducer = reducerMap[action.type];
return reducer
? reducer(params, action.payload ? action.payload : {}, action.params)
: params;
};
export default createReducer(initialState, {
[`${types.TODO_LIST}_PENDING`]: (state) => {
return Object.assign({}, state, { todoListLoading: true });
},
[`${types.TODO_LIST}_ERROR`]: (state) => {
return Object.assign({}, state, { todoListLoading: false });
},
[`${types.TODO_LIST}_SUCCESS`]: (state, data) => {
return Object.assign({}, state, { todoList: data, todoListLoading: false });
},
});
// todoList_state.js
export default {
todoListLoading: false,
todoList: [],
};
可以看到,每写一个页面(功能)都要在 4 个文件里来回跳转。所以首先,最容易想到的就是合并文件,把 action、reducer、type、state 都放在一个文件中处理。这样可以解决来回跳转的问题。
然后再观察一下代码,可以发现沉余的就是 reducer 代码。而这里面主要就是 loading 状态的处理。
参考了许多文章,最终优化代码如下:
// todo.js
// 接口
import { todoListSvr } from "services/card";
// types
export const types = {
TODO_LIST: "TODO_LIST",
};
// actions方法
export const actions = {
todoList: (obj) => ({
type: types.TODO_LIST,
payload: {
// 使用了 promiseMiddleware 中间件
promise: todoListSvr(params),
},
}),
};
// state数据
const initialState = {
todoList: [],
};
// reducer
export default createReducer(initialState, {
[`${types.TODO_LIST}_SUCCESS`]: (state, data) =>
Object.assign({}, state, { todoList: data }),
});
// loadingReducer.js
const loadingReducer = (state = {}, action) => {
const { type } = action;
const matches = /(.*)_(PENDING|SUCCESS|ERROR)/.exec(type);
if (!matches) return state;
const [, requestName, requestState] = matches;
return {
...state,
[requestName]: requestState === "PENDING",
};
};
// 页面内使用:const mapStateToProps = (state) => ({ isLoading: loadingSelector(['TODO_LIST'])(state) })
export const loadingSelector = (actions) => (state) =>
actions.some((action) => state.loadingReducer[action]);
export default loadingReducer;
页面内使用:
import { loadingSelector } from "store/modules/loadingReducer";
const mapStateToProps = (state) => ({
isLoading: loadingSelector(["TODO_LIST"])(state),
});
export default connect(mapStateToProps)(Todo);