通常在redux中写reducer时,根据action传递过来的type,进行判断,数据处理也是在reducer中。当然不用if 就用switch,大多数还用的switch
export default (state = initState, action) => {
switch(action.type) {
case LOGIN:
return Object.assign({}, state, {
type: LOGIN,
data: action.params
})
case LOGIN_SUCCESS:
return Object.assign({}, state, {
type: LOGIN_SUCCESS,
data: action.data
})
case LOGIN_ERROR:
return Object.assign({}, state, {
type: LOGIN_ERROR,
data: action.data
})
default:
return state
}
}
标准的写法,判断多了,写下去感觉也挺麻烦的,能脱离switch就好了,只是把
return Object.assign({}, state, {
type: LOGIN_ERROR,
data: action.data
})
这一段集中在一起, 如这样
const reducer = {
[REQUEST]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_SUCCESS]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_SUCCESS,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_ERROR]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_ERROR,
data: action.data,
namespace: action.namespace
})
}
}
用json格式,就需要根据不同的action,取不同的值,那么再创建一个createReducer.js
export default (initState, reducerTarget) =>
(state = initState, action) => (reducerTarget[action.type]) && reducerTarget[action.type](state, action) || state
这样上面的reducer就通过createReducer高阶函数封装一遍
const reducer = {
[REQUEST]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_SUCCESS]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_SUCCESS,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_ERROR]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_ERROR,
data: action.data,
namespace: action.namespace
})
}
}
export default () => {
return (state = initState, action) => {
return createReducer(state, reducer)(state, action);
}
};
如此,就不用写switch语句了,在combineReducers中
const createReducer = (injectedReducers) => {
return combineReducers({
reducerState: reducer(),
...injectedReducers
})
}
到这里成功摆脱了一个跟一个的判断,但还是有个问题,比如一些简单的请求,都同样的只取三个状态,请求,请求成功,请求错误。那么这个reducer应该可以重用的,不然我们就需对不同的请求写不同的reducer感觉很麻烦。
还是将就上面的reducer,一共有三个状态,当然对于其他请求可能还有不同的请求,对reducer能重用还能加入对应不同的,才是目的, 如对象合并似的
let reducerNew = {...reducer, ...selfReducer}
比如现在我们有两个请求一个请求用户数据,一个请求用户权限,共用同一个reducer
const createReducer = (injectedReducers) => {
return combineReducers({
userState: composeReducers(userReducers(), requestReducer()),
permissionsState: composeReducers(permissionsReducers(), requestReducer()),
...injectedReducers
})
}
这样就比较符合想的结果composeReducers函数
const composeReducers = (...reducers) => {
return (state, action) => {
if (reducers.length === 0) {
return state;
}
let last = reducers[reducers.length - 1],
rest = reducers.slice(0, -1);
return rest.reduceRight((enhanced, reducer) => reducer(enhanced, action),
last(state, action)
);
};
}
userReducers 和 permissionsReducers
const permissionsState = {
permissions: []
}
const userInfo = {
birthday: ''
}
const permissionsReducer = {
}
const userInfoReducer = {
}
export const permissionsReducers = () => {
return (state = permissionsState, action) => {
return createReducer(state, permissionsReducer)(state, action);
}
};
export const userReducers = (namespace) => {
return (state = userInfo, action) => {
return createReducer(state, userInfoReducer)(state, action);
}
};
到此我们来请求一下但问题是需要调用this.props.action,也就是this.props.request 他们用的同一个action,这样返回的type就都是一样的
额,当然action 为了简单的写已经被我写成这样,对于没没有太多数据处理的 action,这样写也能省一点代码
export const requestStatus = {
REQUEST: 'REQUEST',
REQUEST_SUCCESS: 'REQUEST_SUCCESS',
REQUEST_ERROR: 'REQUEST_ERROR'
}
// actionsCreater
export default (actionTypes) => {
let actionKeys = Object.keys(actionTypes),
actions = {};
for (let i = 0, item; item = actionKeys[i++];) {
let funName = actionName(item.toLowerCase());
actions[funName] = (data) => {
return ({
type: item,
...data
})
}
}
return actions;
}
// 返回actions
export default actionsCreater({...requestStatus, ...othersStatus})
那么就需要对不同请求进行区分了,redux官网有介绍用prefix,还有就是给reducer取个别名,在this.props.request里进行传递参数,比如这样
this.props.request({namespace: 'user', data: {access_token: access_token}})
this.props.request({namespace: 'permissions', data: {access_token: access_token}})
如果不区分,当然reducer里也同样存在两数据,但是后面一个的数据会修改前面一个的, 所以createReducer修改一下
const createReducer = (injectedReducers) => {
return combineReducers({
userState: composeReducers(userReducers(), requestReducer('user')),
permissionsState: composeReducers(permissionsReducers(), requestReducer('permissions')),
...injectedReducers
})
}
同样修改createReducer
export default (initState, reducerTarget, reducerNamespace) => {
return (state = initState, action) => {
let { namespace, type } = action;
if (state === undefined || reducerNamespace !== namespace) {
return state;
}
return (reducerTarget[type]) && reducerTarget[type](state, action) || state
}
}
那么requestReducers也做同样修改
import createReducer from '../createReducer';
const { REQUEST, REQUEST_SUCCESS, REQUEST_ERROR } = requestStatus;
const initState = {
type: REQUEST,
data: {}
}
const reducer = {
[REQUEST]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_SUCCESS]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_SUCCESS,
data: action.data,
namespace: action.namespace
})
},
[REQUEST_ERROR]: (state, action) => {
return Object.assign({}, state, {
type: REQUEST_ERROR,
data: action.data,
namespace: action.namespace
})
}
}
export default (namespace) => {
return (state = initState, action) => {
return createReducer(state, reducer, namespace)(state, action);
}
};
是不是完了,没有这样写但是在saga里还是不能给值,因为多了一个 namespase
import { effects } from 'redux-saga';
import { requestStatus } from '../../actions/typeCom';
const {call, put} = effects;
const { REQUEST_SUCCESS, REQUEST_ERROR } = requestStatus;
export function* requestSaga(data) {
try {
// userInfoRequest 请求用户信息
let res = yield call(userInfoRequest, data.data);
yield put({type: REQUEST_SUCCESS, data: res.data, namespace: data.namespace});
return res;
} catch (error) {
yield put({type: REQUEST_ERROR, namespace: data.namespace});
}
}
那么注意一下userInfoRequest,这样像也不对,我需要用同一个saga呢,在复制一个或者在写一个方法把这个requestSaga包装一次,每次需要都包装感觉也不是个事
最好把userInfoRequest当参数传递过去,目前这么想的就在调用请求时
this.props.request(userInfoRequest, {namespace: 'permissions', data: {access_token: access_token}})
但是这样传递给action时,action中return 返回数据是方法是不被saga接收的只能和data一起返回那修改一下actionsCreater
const actionName = str => {
return str.replace(/_(\w)/g, function(all, letter){
return letter.toUpperCase();
})
}
export default (actionTypes) => {
let actionKeys = Object.keys(actionTypes),
actions = {};
for (let i = 0, item; item = actionKeys[i++];) {
let funName = actionName(item.toLowerCase());
actions[funName] = (obj, data) => {
if (typeof obj === 'function') {
return ({
fn: obj,
type: item,
...data
})
} else {
return ({
type: item,
...obj
})
}
}
}
return actions;
}
这样调用request时
this.props.request(adminUserInfo, {namespace: 'user', data: {access_token: access_token}})
this.props.request(operationPermissions, {namespace: 'permissions', data: {access_token: access_token}})
这样多个请求就可以同用部分类似的,报错action, reducer,saga
请求一下,看看localStorage里面的数据,利用PersistGate存下来的
大概如上,有待优化···