Redux架构优化之如何减少样板代码
fengliner opened this issue · 0 comments
fengliner commented
“redux很好,但是有太多的样板代码了” 。
这是大多数使用redux的同学的吐槽点。也正因为此,才诞生了那么多基于redux的优化方案。
1. action utilities for redux: redux-actions
以 redux-actions 为首的 函数工具库。
这类方案通常是利用一个生成action creator的函数或者生成reducer creator的函数来减少使用redux中的样板代码。
1.1 定义makeActionCreator
函数
可以减少简单的action creator函数,对于复杂的(异步)action creator函数不太好处理
function makeActionCreator(type, ...argNames) {
return function(...args) {
let action = { type }
argNames.forEach((arg, index) => {
action[argNames[index]] = args[index]
})
return action
}
}
const ADD_TODO = 'ADD_TODO'
const EDIT_TODO = 'EDIT_TODO'
const REMOVE_TODO = 'REMOVE_TODO'
export const addTodo = makeActionCreator(ADD_TODO, 'todo')
export const editTodo = makeActionCreator(EDIT_TODO, 'id', 'todo')
export const removeTodo = makeActionCreator(REMOVE_TODO, 'id')
1.2 定义createReducer
函数
把每个switch case封装成函数,本质上还是需要对每个action type进行处理
function createReducer(initialState, handlers) {
return function reducer(state = initialState, action) {
if (handlers.hasOwnProperty(action.type)) {
return handlers[action.type](state, action)
} else {
return state
}
}
}
const todosreducer = createReducer([], {
'ADD_TODO': addTodo,
'TOGGLE_TODO': toggleTodo,
'EDIT_TODO': editTodo
});
1.3 redux-actions
提供API:createActions
handleActions
和combineActions
,异步action需要结合redux-promise使用
import { createActions, handleActions, combineActions } from 'redux-actions'
const defaultState = { counter: 10 };
const { increment, decrement } = createActions({
INCREMENT: amount => ({ amount }),
DECREMENT: amount => ({ amount: -amount })
});
const reducer = handleActions({
[combineActions(increment, decrement)](state, { payload: { amount } }) {
return { ...state, counter: state.counter + amount };
}
}, defaultState);
export default reducer;
2. framework based on redux and redux-saga: dva
基于redux+react-router+redux-saga的封装的一整套解决方案的dva或类dva,比如mirror、mickey
核心方法是app.model,用于reducer、initialState、action、saga封装到一起
app.model({
namespace: 'products',
state: {
list: [],
loading: false,
},
subscriptions: [
function(dispatch) {
dispatch({type: 'products/query'});
},
],
effects: {
['products/query']: function*() {
yield call(delay(800));
yield put({
type: 'products/query/success',
payload: ['ant-tool', 'roof'],
});
},
},
reducers: {
['products/query'](state) {
return { ...state, loading: true, };
},
['products/query/success'](state, { payload }) {
return { ...state, loading: false, list: payload };
},
},
});
3. Simple, scalable state management: mobx
在一个地方保存state,通过注解观察state,一旦state修改组件会自动的更新
import {observable, autorun} from 'mobx';
var todoStore = observable({
/* 一些观察的状态 */
todos: [],
/* 推导值 */
get completedCount() {
return this.todos.filter(todo => todo.completed).length;
}
});
/* 观察状态改变的函数 */
autorun(function() {
console.log("Completed %d of %d items",
todoStore.completedCount,
todoStore.todos.length
);
});
/* ..以及一些改变状态的动作 */
todoStore.todos[0] = {
title: "Take a walk",
completed: false
};
// -> 同步打印 'Completed 0 of 1 items'
todoStore.todos[0].completed = true;
// -> 同步打印 'Completed 1 of 1 items'
4. High level abstraction between React and Redux: kea
基于redux、redux-saga、reselect定义logic文件,通过注解的方式引用logic文件里定义的actions、reducers
const logic = kea({
actions: () => ({
increment: (amount) => ({ amount }),
decrement: (amount) => ({ amount })
}),
reducers: ({ actions }) => ({
counter: [0, PropTypes.number, {
[actions.increment]: (state, payload) => state + payload.amount,
[actions.decrement]: (state, payload) => state - payload.amount
}]
}),
selectors: ({ selectors }) => ({
doubleCounter: [
() => [selectors.counter],
(counter) => counter * 2,
PropTypes.number
]
})
})
class Counter extends Component {
render () {
const { counter, doubleCounter } = this.props
const { increment, decrement } = this.actions
return <div>...</div>
}
}
export default logic(Counter)
小结
- redux-actions:从action层面解决redux样板代码的问题,仅仅是一个库(library)
- dva和kea:不同于redux-actions只提供一个简单的库(library),二者都是基于redux及redux的一些异步方案(redux-saga、redux-thunk等)封装的一套框架(framework)。而且二者的设计理念也很相似,都是把action、reducer、saga封装到一个logic或model里,然后把logic或model和组件connect起来进行引用。不同的是kea大量使用了注解(decorator),进一步减少样板代码。
- mobx:redux的替代方案,不同于redux的纯函数(pure function)的设计,深受面向对象(oo)和命令式编程( imperative)的影响
“redux很好,但是有太多的样板代码了” 。
现在可以不用再吐槽了。