1.前言为什么要介绍redux-actions?第一次看到主要是为了接手公司原来的项目,发现以前的一些老板在处理redux的时候介绍过。也发现它让redux的处理方便了很多,为了更好的使用一个组件或者插件,我会尝试去读源码写一篇文章,这次也不例外。我发现它真的很有趣。我建议大家在使用redux的时候也引入redux-actions。我这里介绍一下它的用法,手工实现一个简单的redux-actions。2.介绍在学习redux的时候,总觉得action和reducer的代码太生硬了,比如2.1createactionletincrement=()=>({type:"increment"})2.2reducerletreducer=(state,action)=>{switch(action.type){case"increment":return{count:state.count+1};break;case"decrement":return{count:state.count-1};break;default:returnstate;}}2.3Triggeractiondispatch(increment())综上所述,我们难免会觉得increment和reducer做个小demo还可以,但是遇到逻辑复杂的项目,项目管理和维护就会显劣势。所以最后一种方式就是将它们分开,同时让开发者在reducer上有更多的主动权,而不仅仅是数字的增减。redux-actions的主要功能包括createAction、createActions、handleAction、handleActions和combineActions。基本上只用到createAction、handleActions、handleAction,所以这里只讨论这三个。3.识别手写createAction()3.1用法创建Action的一般方式:letincrement=()=>({type:"increment"})letincrementObj=increment();//{type:"increment"}使用createAction创建actionimport{createAction}from'redux-actions';constincrement=createAction('increment');letincrementObj=increment();//{type:"increment"}letincrement=increment(10);//{type:"increment",paylaod:10}我们可以看到letincrement=()=>({type:"increment"})letincrementObj=increment();//{type:"increment"}和constincrement=createAction('increment');letincrementObj=increment();//{type:"increment"}是等价的,为什么不直接用传统的方式呢?不难发现两点:传统方式需要写一个函数返回incrementObj,使用封装好的createAtion不需要传统方式自己写函数。如果返回的incrementObj中有payload,需要自己添加。这是一件多么麻烦的事情。看下面的代码,太不方便了。但是使用createAction返回的增量,我们添加有效负载,这非常简单。直接传一个参数,它会直接作为payload的值。letincrement=()=>({type:"increment",payload:123})3.2原理实现先来实现一个简单的,将值传入type参数,即实现下面代码constincrement=的功能createAction('increment');letincrementObj=increment();//{type:"increment"}我们发现createAction('increment')()返回的是最终的动作对象。这不只是一个咖喱函数吗?所以我们可以写的很简单,如下代码所示,我们将类型type作为action对象的一个??属性functioncreateAction(type){return()=>{constaction={type};returnaction;};}就可以了现在,现在实现下面的函数,即如果有一个payloadconstincrement=createAction('increment');letobjincrement=increment(10);//{type:"increment",paylaod:10}显然,这个payload是createAction('increment')返回的函数的参数,因此我们可以轻松地向操作添加有效负载。functioncreateAction(type){return(payload)=>{constaction={type,payload};returnaction;};}但是像第一种情况,我们不传递payload,也就是说返回的action不想carrythepayload是的,但是这里我们是这样写的,也就是说默认必须传入payload。所以我们需要添加一个判断,当没有传递payload时,action不会添加payload属性。functioncreateAction(type){return(payload)=>{constaction={type,};if(payload!==undefined){action.payload=payload}returnaction;};}在实际项目中,我比较喜欢下面的写法,但是相当于上面的函数createAction(type){return(payload)=>{constaction={type,...payload?{payload}:{}};returnaction;};}其实createAction是另外加的对于type参数,还可以传入一个回调函数,代表对payload的处理。constincrement=createAction('increment');letobjincrement=increment(10);//{type:"increment",paylaod:10}如上代码所示,我们要的是传入10,然后返回actionpayload是我们传入的2的倍数constincrement=createAction('increment',(t)=>t*2);letobjincrement=increment(10);//{type:"increment",paylaod:20}现在,让一起让它成为现实。functioncreateAction(type,payloadCreator){return(payload)=>{constaction={type,};if(payload!==undefined){action.payload=payloadCreator(payload)}返回动作;};}functioncreateAction(type,payloadCreator){return(payload)=>{constaction={type,};if(payload!==undefined){action.payload=payloadCreator(payload)}returnaction;};}太简单了!但是我们又犯了一个和之前一样的错误,就是我们在使用createAction的时候,回调函数payloadCreator可能没有传入,所以我们需要判断函数createAction(type,payloadCreator){return(payload)=>{constaction={类型,};if(payload!==undefined){action.payload=payloadCreator?payloadCreator(payload):payload}returnaction;};}完美。接下来我们看一下redux-action的handleActions。四、理解handleActions,我们先来看看传统reducer是怎么使用的。letreducer=(state,action)=>{switch(action.type){case"increment":return{count:state.count+1};break;case"decrement":return{count:state.count-1};break;default:returnstate;}}看看handleActions的使用payload}),[DECREMENT]:(state,action)=>({counter:state.counter-action.payload})},initstate)在这里,不要被{[DECREMENT]:()的写法吓到{}},它只是将属性写为变量。我们在控制台console.log(reducer)中查看结果,最后返回一个reducer函数。这样,reducer中功能化的自由度就实现了。如果我们要写任何程序,只需要写在函数{[increment]:(state,action)=>{}}中,也可以把这些函数单独放到一个文件中,然后导入import{递增,递减}来自“./reducers.js”varinitstate={count:0}varreducer=createReducer({[INCREMENT]:increment,[DECREMENT]:decrement},initstate)reducers.js//reducers.jsexportletincrement=(state,action)=>({counter:state.counter+action.payload})exportletdecrement=(state,action)=>({counter:state.counter-action.payload})可见,handleactions可以简化reducer的写法没有那么多开关,可以把功能分开,这样reducer就不会再有很多代码了。本来是要说handleActions的实现的,但是在这之前,必须先说说handleAction,是的,你仔细看,没有s五、理解和手写实现handleAction5.1的用法看constincrementReducer=handleAction的使用方法(INCREMENT,(state,action)=>{return{counter:state.counter+action.payload}},initialState);可以看出,与handleActions的区别在于,handleAction生成的reducer是专门用来处理一个action的。5.2原理实现如果你看过redux的原理(如果没看过,推荐你看我之前的文章Redux源码分析系列(一)——Redux的实现思路),相信你应该知道reducer(state,action)返回一个新状态,然后将新状态与旧状态进行比较。如果发现两者不同,则使用该状态的组件将被重新渲染,并将新状态分配给旧状态。国家。也就是说,handleAction()返回一个reducer函数,然后incrementReducer()返回一个新的状态。首先实现并返回一个reducer函数functionhandleAction(type,callback){return(state,action)=>{};}接下来应该是执行reducer(state,action)返回状态的时候了,也就是执行(下面返回的状态),动作)=>{};其实就是执行callback(state)然后返回一个新的statefunctionhandleAction(type,callback){return(state,action)=>{returncallback(state)};}也许你会有疑惑,为什么要这样做,而不是直接像下面这样,少了一层包容。functionhandleAction(state,type,callback){returncallback(state)}这就是它的巧妙之处。当handleAction()返回的reducer()不一定执行callback(state)时,只有handleAction传入的type与reducer()传入的action.type匹配才会执行,否则直接返回state。表示没有处理函数handleAction(type,callback){return(state,action)=>{returncallback(state)};}所以需要加一层判断函数handleAction(type,callback){return(state,action)=>{if(action.type!==type){returnstate;}returncallback(state)};}多么完美!下面我们来实现handleActions6.handleActions实现的原理functionhandleActions(handlers,defaultState){construducers=Object.keys(handlers).map(type=>{returnhandleAction(type,handlers[type]);});constreducer=reduceReducers(...reducers)return(state=defaultState,action)=>reducer(state,action)}看,就这几行代码,是不是很简单,但是应该很难理解,但是没关系,我还是用通俗易懂的方式解释一下。我们以上面使用的例子为例varreducer=handleActions({[INCREMENT]:(state,action)=>({counter:state.counter+action.payload}),[DECREMENT]:(state,action)=>({counter:state.counter-action.payload})},initstate){[INCREMENT]:(state,action)=>({counter:state.counter+action.payload}),[DECREMENT]:(状态,action)=>({counter:state.counter-action.payload})}上面的对象,在下面的代码construducers=Object.keys(handlers).map(type=>{returnhandleAction(type,handlers[type]);});返回的reducer其实是[handleAction(INCREMENT,(state,action)=>({counter:state.counter+action.payload})),handleAction(DECREMENT,(state,action)=>({counter:state.counter+action.payload})),]为什么要变成handleAction的数组呢?我大概想到了。我想在每次dispatch(action)的时候遍历执行这个数组。所有handleActions。不是每个handleAction返回的reducer都要执行吗?确实,但是不要忘记我们上面说的,如果handleAction判断了type和action.type,那么它不会处理state而是直接返回statefunctionhandleAction(type,callback){return(state,action)=>{if(action.type!==type){returnstate;}returncallback(state)};}不行,就算每一个handleAction都执行了,怎么遍历执行也无所谓,用map,forEach?不,都不是。我们回头看下源码)return(state=defaultState,action)=>reducer(state,action)}使用constreducer=reduceReducers(...reducers)使用reduceReducers方法,顾名思义,看方法名,意思是使用reduce来遍历并执行reducers数组。也就是这个数组。[handleAction(INCREMENT,(state,action)=>({counter:state.counter+action.payload})),handleAction(DECREMENT,(state,action)=>({counter:state.counter+action.payload})),]看看reduceReducers的内部原理,值);},prevState);};};我们发现将reducer数组放入reduceReducers中,然后执行reduceReducers,会返回(prevState,value)=>{returnreducers.reduce((newState,reducer,index)=>{returnreducer(newState,value);},prevState);};这个方法,也就是说执行这个方法会执行returnreducers.reduce((newState,reducer,index)=>{returnreducer(newState,value);},prevState);也就是说,将使用reducer来遍历和执行reducer。为什么要用reduce来遍历?这是因为上一个handleAction执行后返回的state需要传递给下一个。这个想法有点像我们之前讲过的compose函数的想法。有兴趣的可以看看【前端进阶理解及手写Compose方法】类型]);});constreducer=reduceReducers(...reducers)return(state=defaultState,action)=>reducer(state,action)}现在就是这里的reducer是reduceReducers(...reducers)返回的结果,即reducer=(prevState,value)=>{returnreducers.reduce((newState,reducer,index)=>{returnreducer(newState,value);},prevState);};而handleActions返回的是(state=defaultState,action)=>reducer(state,action),也就是说handleActions实际上返回了这样一个方法。(state=defaultState,action)=>{returnreducers.reduce((newState,reducer,index)=>{returnreducer(newState,value);},state);}好家伙,在handleAction之间用reduce来传递state,好厉害很好的学习方式。贴出github的redux-action的源码地址。感兴趣的朋友可以亲自阅读。毕竟这篇文章是一个简化的redux-actions:https://github.com/redux-utilities/redux-actions参考文章【React系列---FSA知识】https://segmentfault.com/a/1190000010113847【用法redux-actions的实现】https://zhuanlan.zhihu.com/p/273569290【前端进阶理解及手写compose方法】Redux源码分析系列(一)——Redux实现思路)
