react使用redux时reducer的重用和去switch判断

通常在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存下来的
在这里插入图片描述

大概如上,有待优化···

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章