We read every piece of feedback, and take your input very seriously.
To see all available qualifiers, see our documentation.
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
为什么介绍redux-actions呢?
第一次见到主要是接手公司原有的项目,发现有之前的大佬在处理redux的时候引入了它。
发现也确实 使得 在对redux的处理上方便了许多,而我为了更好地使用一个组件或者插件,都会去去尝试阅读源码并写成文章 ,这个也不例外。
发现也确实有意思,推荐大家使用redux的时候也引入redux-actions
在这里就介绍一下其使用方式,并且自己手写实现一个简单的redux-actions
在学习 redux 中,总觉得 action 和 reducer 的代码过于呆板,比如
let increment = ()=>({type:"increment"})
let reducer = (state,action)=>{ switch(action.type){ case "increment":return {count:state.count+1};break; case "decrement":return {count:state.count-1};break; default:return state; } }
dispatch(increment())
综上所示,我们难免会觉得 increment 和 reducer 做一个小 demo 还行,遇到逻辑偏复杂的项目后,项目管理维护就呈现弊端了。所以最后的方式就是将它们独立出来,同时在 reducer 中给与开发者更多的主动权,不能仅停留在数字的增增减减。
redux-actions主要函数有createAction、createActions、handleAction、handleActions、combineActions。
基本上就是只有用到createAction,handleActions,handleAction
所以这里我们就只讨论这三个个。
一般创建Action方式:
let increment = ()=>({type:"increment"}) let incrementObj = increment();// { type:"increment"}
使用createAction 创建 action
import { createAction } from 'redux-actions'; const increment = createAction('increment'); let incrementObj = increment();// { type:"increment"} let objincrement = increment(10);// {type:"increment",paylaod:10}
我们可以看到
与
const increment = createAction('increment'); let incrementObj = increment();// { type:"increment"}
是等效的,那为什么不直接用传统方式呢?
不难发现有两点:
let increment = ()=>({type:"increment",payload:123})
我们先实现个简单,值传入 type参数的,也就是实现下面这段代码的功能
我们发现createAction('increment')()才返回最终的action对象。这不就是个柯里化函数吗?
createAction('increment')()
所以我们可以非常简单的写出来,如下面代码所示,我们把type类型当作action对象的一个属性了
function createAction(type) { return () => { const action = { type }; return action; }; }
好了现在,现在实现下面这个功能,也就是有payload的情况
const increment = createAction('increment'); let objincrement = increment(10);// {type:"increment",paylaod:10}
很明显,这个payload是 在createAction('increment')返回的函数的参数,所以我们轻而易举地给action添加上了payload。
在createAction('increment')
function createAction(type) { return (payload) => { const action = { type, payload }; return action; }; }
但是像第一种情况我们是不传payload的,也就是说返回的action是不希望带有payload的,但是这里我们写成这样就是 默认一定要传入payload的了。
所以我们需要添加个判断,当不传payload的时候,action就不添加payload属性。
function createAction(type) { return (payload) => { const action = { type, }; if(payload !== undefined){ action.payload = payload } return action; }; }
在实际项目中我更喜欢下面这种写法,但它是等价于上面这种写法的
function createAction(type) { return (payload) => { const action = { type, ...payload?{payload}:{} }; return action; }; }
其实createAction的参数除了type,还可以传入一个回调函数,这个函数表示对payload的处理。
像上面的代码所示,我们希望的是传入10之后是返回的action中的payload是我们传入的2倍数
const increment = createAction('increment',(t)=> t * 2); let objincrement = increment(10);// {type:"increment",paylaod:20}
现在,就让我们实现一下。
function createAction(type,payloadCreator) { return (payload) => { const action = { type, }; if(payload !== undefined){ action.payload = payloadCreator(payload) } return action; }; }
卧槽,太简单了吧! 但是我们又犯了前边同样的错误,就是我们使用createAction的时候,不一定会传入payloadCreator这个回调函数,所以我们还需要判断下
function createAction(type,payloadCreator) { return (payload) => { const action = { type, }; if(payload !== undefined){ action.payload = payloadCreator?payloadCreator(payload):payload } return action; }; }
卧槽,完美。
接下來看看 redux-action的 handleActions吧
我们先看看传统的reducer是怎么使用的
再看看使用了handleActions
const INCREMENT = "increment" const DECREMENT = "decrement" var reducer = handleActions({ [INCREMENT]: (state, action) => ({ counter: state.counter + action.payload }), [DECREMENT]: (state, action) => ({ counter: state.counter - action.payload }) },initstate)
这里大家不要被{[DECREMENT]:(){}} 的写法吓住哈,就是把属性写成变量了而已。
{[DECREMENT]:(){}}
我们在控制台 console.log(reducer) 看下结果
最后返回的就是一个 reducer 函数。
这样就实现了 reducer 中功能化的自由,想写什么程序,我们只要写在
{[increment]:(state,action)=>{}}
这个函数内就行,同时也可以把这些函数独立成一个文件,再引入进来就行
import {increment,decrement}from "./reducers.js" var initstate = {count:0} var reducer = createReducer({ [INCREMENT]: increment, [DECREMENT]: decrement },initstate)
reducers.js
//reducers.js export let increment = (state,action)=>({counter: state.counter + action.payload}) export let decrement = (state,action)=>({counter: state.counter - action.payload})
可见,
handleactions 可以简化 reducers 的写法 不用那么多 switch 而且可以把函数独立出来,这样reducer就再也不会有一大堆代码了。
本来要讲handleActions的实现了,但是在这之前,我们必须先讲一下handleAction,对,你仔细看,没有s
看下使用方式
const incrementReducer = handleAction(INCREMENT, (state, action) => { return {counter: state.counter + action.payload} }, initialState);
可以看出来,跟handleActions的区别 就是,handleAction生成的reducer是专门来处理一个action的。
如果你看过redux原理的话(如果你没看过的话,推荐你去看下我之前的文章Redux 源码解析系列(一) -- Redux的实现思想),相信你应该知道reducer(state,action)返回的结果是一个新的state,然后这个新的state会和旧的state进行对比,如果发现两者不一样的话,就会重新渲染使用了state的组件,并且把新的state赋值给旧的state.
也就是说handleAction()返回一个reducer函数,然后incrementReducer()返回一个新的state。
handleAction()
incrementReducer()
先实现返回一个reducer函数
function handleAction(type, callback) { return (state, action) => { }; }
接下来应当是执行reducer(state,action)是时候返回state,也就是执行下面返回的这个
(state, action) => { };
而其实就是执行callback(state) 然后返回一个新的 state
function handleAction(type, callback) { return (state, action) => { return callback(state) }; }
或许你会有疑问,为什么要这么搞,而不直接像下面这样,就少了一层包含。
function handleAction(state,type, callback) { return callback(state) }
这才是它的巧妙之处。它在handleAction()返回的reducer()时,可不一定会执行callback(state),只有handleAction传入的type跟reducer()中传入的action.type匹配到了才会执行,否则就直接return state。表示没有任何处理
因此我们需要多加一层判断
function handleAction(type, callback) { return (state, action) => { if (action.type !== type) { return state; } return callback(state) }; }
多么完美啊!
好了现在我们来实现下handleActions
function handleActions(handlers, defaultState) { const reducers = Object.keys(handlers).map(type => { return handleAction(type, handlers[type]); }); const reducer = reduceReducers(...reducers) return (state = defaultState, action) => reducer(state, action) }
看,就这几行代码,是不是很简单,不过应该不好理解,不过没关系,我依旧将它讲得粗俗易懂。
我们拿上面用到的例子来讲好了
var reducer = 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]: (state, action) => ({ counter: state.counter - action.payload }) }
上面这个对象,经过下面的代码之后
const reducers = Object.keys(handlers).map(type => { return handleAction(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)的时候,就要遍历去执行这个数组中的所有handleAction。
那岂不是每个handleAction返回的reducer都要执行? 确实,但是别忘了我们上面讲到的,如果handleAction 判断到 type和action.type 是不会对state进行处理的而是直接返回state
没有即使每个 handleAction 都执行了也没关系
那应该怎么遍历执行,用map,forEach? 不,都不对。我们看回源码
使用了
const reducer = reduceReducers(...reducers)
用了reduceReducers这个方法,顾名思义,看这方法名,意思就是用reduce这个来遍历执行reducers这个数组。也就是这个数组。
我们看下reduceReducers的内部原理
function reduceReducers(...args) { const reducers = args; return (prevState, value) => { return reducers.reduce((newState, reducer, index) => { return reducer(newState, value); }, prevState); }; };
我们发现将reducers这个数组放入reduceReducers,然后执行reduceReducers,就会返回
(prevState, value) => { return reducers.reduce((newState, reducer, index) => { return reducer(newState, value); }, prevState); };
这个方法,也就是说执行这个方法就会 执行
return reducers.reduce((newState, reducer, index) => { return reducer(newState, value); }, prevState);
也就是会使用reduce遍历执行reducers,为什么要用reduce来遍历呢?
这是因为需要把上一个handleAction执行后返回的state传递给下一个。
这个思想有一点我们之间之前讲的关于compose函数的思想,感兴趣的话,可以去看一下【前端进阶之认识与手写compose方法】
现在也就是说这里的reducer是reduceReducers(...reducers)返回的结果,也就
reduceReducers(...reducers)
reducer = (prevState, value) => { return reducers.reduce((newState, reducer, index) => { return reducer(newState, value); }, prevState); };
而handleActions返回
(state = defaultState, action) => reducer(state, action)
也就是说handleActions其实是返回这样一个方法。
(state = defaultState, action) => { return reducers.reduce((newState, reducer, index) => { return reducer(newState, value); }, state); }
好家伙,在handleAction之间利用reduce来传递state,真是个好方法,学到了。
贴一下github 的redux-action的源码地址,感兴趣的朋友可以亲自去阅读一下,毕竟本文是做了简化的 redux-actions:https://github.com/redux-utilities/redux-actions
文章首发于公众号《前端阳光》,欢迎加入技术交流群。
The text was updated successfully, but these errors were encountered:
No branches or pull requests
一、前言
为什么介绍redux-actions呢?
第一次见到主要是接手公司原有的项目,发现有之前的大佬在处理redux的时候引入了它。
发现也确实 使得 在对redux的处理上方便了许多,而我为了更好地使用一个组件或者插件,都会去去尝试阅读源码并写成文章 ,这个也不例外。
发现也确实有意思,推荐大家使用redux的时候也引入redux-actions
在这里就介绍一下其使用方式,并且自己手写实现一个简单的redux-actions
二、介绍
在学习 redux 中,总觉得 action 和 reducer 的代码过于呆板,比如
2.1 创建action
2.2 reducer
2.3 触发action
综上所示,我们难免会觉得 increment 和 reducer 做一个小 demo 还行,遇到逻辑偏复杂的项目后,项目管理维护就呈现弊端了。所以最后的方式就是将它们独立出来,同时在 reducer 中给与开发者更多的主动权,不能仅停留在数字的增增减减。
redux-actions主要函数有createAction、createActions、handleAction、handleActions、combineActions。
基本上就是只有用到createAction,handleActions,handleAction
所以这里我们就只讨论这三个个。
三、 认识与手写createAction()
3.1 用法
一般创建Action方式:
使用createAction 创建 action
我们可以看到
与
是等效的,那为什么不直接用传统方式呢?
不难发现有两点:
3.2 原理实现
我们先实现个简单,值传入 type参数的,也就是实现下面这段代码的功能
我们发现
createAction('increment')()
才返回最终的action对象。这不就是个柯里化函数吗?所以我们可以非常简单的写出来,如下面代码所示,我们把type类型当作action对象的一个属性了
好了现在,现在实现下面这个功能,也就是有payload的情况
很明显,这个payload是
在createAction('increment')
返回的函数的参数,所以我们轻而易举地给action添加上了payload。但是像第一种情况我们是不传payload的,也就是说返回的action是不希望带有payload的,但是这里我们写成这样就是 默认一定要传入payload的了。
所以我们需要添加个判断,当不传payload的时候,action就不添加payload属性。
在实际项目中我更喜欢下面这种写法,但它是等价于上面这种写法的
其实createAction的参数除了type,还可以传入一个回调函数,这个函数表示对payload的处理。
像上面的代码所示,我们希望的是传入10之后是返回的action中的payload是我们传入的2倍数
现在,就让我们实现一下。
卧槽,太简单了吧! 但是我们又犯了前边同样的错误,就是我们使用createAction的时候,不一定会传入payloadCreator这个回调函数,所以我们还需要判断下
卧槽,完美。
接下來看看 redux-action的 handleActions吧
四、认识handleActions
我们先看看传统的reducer是怎么使用的
再看看使用了handleActions
这里大家不要被
{[DECREMENT]:(){}}
的写法吓住哈,就是把属性写成变量了而已。我们在控制台 console.log(reducer) 看下结果
最后返回的就是一个 reducer 函数。
这样就实现了 reducer 中功能化的自由,想写什么程序,我们只要写在
这个函数内就行,同时也可以把这些函数独立成一个文件,再引入进来就行
reducers.js
可见,
handleactions 可以简化 reducers 的写法 不用那么多 switch 而且可以把函数独立出来,这样reducer就再也不会有一大堆代码了。
本来要讲handleActions的实现了,但是在这之前,我们必须先讲一下handleAction,对,你仔细看,没有s
五、认识与手写实现handleAction
5.1 用法
看下使用方式
可以看出来,跟handleActions的区别 就是,handleAction生成的reducer是专门来处理一个action的。
5.2 原理实现
如果你看过redux原理的话(如果你没看过的话,推荐你去看下我之前的文章Redux 源码解析系列(一) -- Redux的实现思想),相信你应该知道reducer(state,action)返回的结果是一个新的state,然后这个新的state会和旧的state进行对比,如果发现两者不一样的话,就会重新渲染使用了state的组件,并且把新的state赋值给旧的state.
也就是说
handleAction()
返回一个reducer函数,然后incrementReducer()
返回一个新的state。先实现返回一个reducer函数
接下来应当是执行reducer(state,action)是时候返回state,也就是执行下面返回的这个
而其实就是执行callback(state) 然后返回一个新的 state
或许你会有疑问,为什么要这么搞,而不直接像下面这样,就少了一层包含。
这才是它的巧妙之处。它在handleAction()返回的reducer()时,可不一定会执行callback(state),只有handleAction传入的type跟reducer()中传入的action.type匹配到了才会执行,否则就直接return state。表示没有任何处理
因此我们需要多加一层判断
多么完美啊!
好了现在我们来实现下handleActions
六、handleActions原理实现
看,就这几行代码,是不是很简单,不过应该不好理解,不过没关系,我依旧将它讲得粗俗易懂。
我们拿上面用到的例子来讲好了
上面这个对象,经过下面的代码之后
返回的reducer,其实就是
为什么要变成一个handleAction的数组,
我大概想到了,是想每次dispatch(action)的时候,就要遍历去执行这个数组中的所有handleAction。
那岂不是每个handleAction返回的reducer都要执行? 确实,但是别忘了我们上面讲到的,如果handleAction 判断到 type和action.type 是不会对state进行处理的而是直接返回state
没有即使每个 handleAction 都执行了也没关系
那应该怎么遍历执行,用map,forEach? 不,都不对。我们看回源码
使用了
用了reduceReducers这个方法,顾名思义,看这方法名,意思就是用reduce这个来遍历执行reducers这个数组。也就是这个数组。
我们看下reduceReducers的内部原理
我们发现将reducers这个数组放入reduceReducers,然后执行reduceReducers,就会返回
这个方法,也就是说执行这个方法就会 执行
也就是会使用reduce遍历执行reducers,为什么要用reduce来遍历呢?
这是因为需要把上一个handleAction执行后返回的state传递给下一个。
这个思想有一点我们之间之前讲的关于compose函数的思想,感兴趣的话,可以去看一下【前端进阶之认识与手写compose方法】
现在也就是说这里的reducer是
reduceReducers(...reducers)
返回的结果,也就而handleActions返回
也就是说handleActions其实是返回这样一个方法。
好家伙,在handleAction之间利用reduce来传递state,真是个好方法,学到了。
贴一下github 的redux-action的源码地址,感兴趣的朋友可以亲自去阅读一下,毕竟本文是做了简化的 redux-actions:https://github.com/redux-utilities/redux-actions
最后
文章首发于公众号《前端阳光》,欢迎加入技术交流群。
参考文章
The text was updated successfully, but these errors were encountered: