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

16 篇文章 1 订阅
15 篇文章 0 订阅

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

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

  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
React-Redux是一个用于在React应用程序集成Redux状态管理库的库。Reducer是Redux的一个核心概念,用于描述状态的变化。它是一个纯函数,接收当前状态和一个动作作为参数,并返回一个新的状态。 在React-Redux,我们通常会创建一个或多个Reducer来管理应用程序的各个部分的状态。Reducer可以单独或组合使用,以便处理不同部分的状态更新。 一个典型的Redux reducer函数看起来像这样: ```javascript const initialState = { // 初始状态 counter: 0, }; const reducer = (state = initialState, action) => { switch (action.type) { case 'INCREMENT': return { ...state, counter: state.counter + 1, }; case 'DECREMENT': return { ...state, counter: state.counter - 1, }; default: return state; } }; ``` 上面的例子,我们定义了一个初始状态initialState对象和一个reducer函数。reducer函数接收两个参数,state和action。根据不同的action类型,在switch语句对状态进行相应的更新,并返回新的状态。在这个例子,我们定义了两种操作类型:INCREMENT和DECREMENT,分别用于增加和减少counter字段的。 在React-Redux,我们可以使用combineReducers函数将多个reducer组合为一个根reducer,并将其传递给Redux store。这样,在应用程序的不同组件就可以通过连接到store来访问和更新相应的状态了。 希望这个回答对你有所帮助!如果你需要更多关于React-Reduxreducer的信息,请随提问。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值