通常在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存下來的
大概如上,有待優化···