redux(一、入门)
0. redux入门精简例子。
链接:点我。看完这个例子,能够快速入门redux。 (耗时半天到一天)。
redux
下面是做的一些笔记,看完这个例子和我有相同的疑惑,希望下面的内容可以帮助到您。
1. combineReducers(args)
记住:普通reducer计算之后,store.getState()返回的是state,例如{name:"zhangsan", age:20}。combineReducer计算之后,返回的store.getState()返回的是带有key的state,例如{ user: {name:"zhangsan", age:20}, item:{price:100, weight:20}}。
1.每个reduce都要有一个default输出默认的state(就算不使用combineReducer也需要保持一个default,返回当前的state,不然state会丢失)
例如:
var userReducer = function (state = {}, action) { console.log('userReducer was called with state', state, 'and action', action) switch (action.type) { // etc. default: return state; } } var itemsReducer = function (state = [], action) { console.log('itemsReducer was called with state', state, 'and action', action) switch (action.type) { // etc. default: return state; } }
否则,当action传入的是user的action且item没有对应的action,这个时候itemReduce如果没有default返回state,那么通过计算过后,itemReducer的state会丢失。
2.combineReducer的参数是一个对象,key是自定义的,value是对应的单个reducer
var reducer = combineReducers({ user: userReducer, items: itemsReducer })
3.不要在reducer中对默认的type
{ type: ActionTypes.INIT }
进行处理。
因为在
createStore(reducer)
的时候就会执行一次
dispatch({ type: ActionTypes.INIT });
在dispatch中就会执行一次reducer。如果在reducer对
{ type: ActionTypes.INIT }
进行了处理,意味着此时计算了一次reducer,返回了一个新的状态。最好是不要处理这个action={ type: ActionTypes.INIT },通过default返回默认的state
2. connect()
帮助理解 thunkMiddleware(自定义的)、applyMiddleware(工具类)的使用:
最终的store_0就是定义在applyMiddleware的
return {
...store,
dispatch
}
action(dispatch,getState)相当于执行的是asyncSayActionCreator_1返回的函数:function(dispatch){setTimeout(……)}
我们看到action(dispatch,getState)传入的getState在function(dispatch){setTimeout(……)}没有用到, 我们可以吧function(dispatch){setTimeout(……)}添加一个参数获取getState函数句柄。
运行:npm run example 09_middleware.js
控制台多一条打印内容:getState() { speaker: { message: 'Hi' } } ,
可以看到我们可以在这个地方获取到旧的state,dispatch之后,还可以得到新的state
在store_0.dispatch(xxx)之前,订阅监听器更新视图,那么,每次dispatch产生新的state,监听器自动执行
store_0.subscribe(function() { console.log('store_0 has been updated. Latest store state:', store_0.getState()); // 在这里更新你的视图 })
源码:
1 // 章节 9 - middleware.js 2 3 // 在 dispatch-async-action-2.js 章节中我们抛出了“中间件”的概念。中间件似乎 4 // 可以帮助我们处理异步 action。但中间件到底是什么呢? 5 6 // 通常来说中间件是在某个应用中 A 和 B 部分中间的那一块, 7 // 中间件可以把 A 发送数据到 B 的形式从 8 // A -----> B 9 // 变成: 10 // A ---> middleware 1 ---> middleware 2 ---> middleware 3 --> ... ---> B 11 12 // 那么中间件在 Redux 中是如何工作的? 13 // 看上去 Redux 并不能自动处理 action creator 中返回的异步函数。 14 // 但如果在 action creator 和 reducer 之间增加一个中间件,就可以把这个函数转成 15 // 适合 Redux 处理的内容: 16 17 // action ---> dispatcher ---> middleware 1 ---> middleware 2 ---> reducers 18 19 // 每当一个 action(或者其他诸如异步 action creator 中的某个函数)被分发时, 20 // 我们的中间件就会被调用 21 // 并且在需要的时候协助 action creator 分发真正的 action(或者什么都不做, 22 // 有时我们需要这么做) 23 24 // 在 Redux 中,中间件是纯粹的函数, 25 // 有明确的使用方法并且严格的遵循以下格式: 26 /* 27 var anyMiddleware = function ({ dispatch, getState }) { 28 return function(next) { 29 return function (action) { 30 // 你的中间件业务相关代码 31 } 32 } 33 } 34 */ 35 36 // 如上所述,中间件由三个嵌套的函数构成(会依次调用): 37 // 1) 第一层向其余两层提供分发函数和 getState 函数 38 // (因为你的中间件或 action creator 可能需要从 state 中读取数据) 39 // 2) 第二层提供 next 函数,它允许你显式的将处理过的输入传递给下一个中间件或 Redux 40 // (这样 Redux 才能调用所有 reducer)。 41 // 3) 第三层提供从上一个中间件或从 dispatch 传递来的 action, 42 // 这个 action 可以调用下一个中间件(让 action 继续流动) 或者 43 // 以想要的方式处理 action。 44 45 // 学习过函数式编程的人可能会意识到给上述代码提供了一个机会来使用 46 // 柯里化(如果你不理解也没关系,跳过接下去的 10 行,不会影响你对 redux 的理解)。 47 // 使用柯里化,你可以简化上述函数: 48 /* 49 // "curry" may come any functional programming library (lodash, ramda, etc.) 50 var thunkMiddleware = curry( 51 ({dispatch, getState}, next, action) => ( 52 // 你的中间件业务相关代码 53 ) 54 ); 55 */ 56 57 // 我们为异步 action creator 提供的中间件叫 thunk middleware 58 // 它的代码在:https://github.com/gaearon/redux-thunk. 59 // 它看上去是这样 (为了可读性使用 ES5 语法书写该函数): 60 61 var thunkMiddleware = function ({ dispatch, getState }) { 62 // console.log('Enter thunkMiddleware'); 63 return function(next) { 64 // console.log('Function "next" provided:', next); 65 return function (action) { 66 // console.log('Handling action:', action); 67 return typeof action === 'function' ? 68 action(dispatch, getState) : 69 next(action) 70 } 71 } 72 } 73 74 // 为了让 Redux 知道我们有一个或多个中间件,我们使用 Redux 的 75 // 辅助函数:applyMiddleware. 76 77 // applyMiddleware 接收所有中间件作为参数,返回一个供 Redux createStore 调用的函数。 78 // 当最后这个函数被调用时,它会产生一个 Store 增强器,用来将所有中间件应用到 Store 的 dispatch 上。 79 // (来自 https://github.com/rackt/redux/blob/v1.0.0-rc/src/utils/applyMiddleware.js) 80 81 // 下面就是如何将一个中间件应用到 Redux store: 82 83 import { createStore, combineReducers, applyMiddleware } from 'redux' 84 85 const finalCreateStore = applyMiddleware(thunkMiddleware)(createStore) 86 // 针对多个中间件, 使用:applyMiddleware(middleware1, middleware2, ...)(createStore) 87 88 var reducer = combineReducers({ 89 speaker: function (state = {}, action) { 90 console.log('speaker was called with state', state, 'and action', action) 91 92 switch (action.type) { 93 case 'SAY': 94 return { 95 ...state, 96 message: action.message 97 } 98 default: 99 return state 100 } 101 } 102 }) 103 104 const store_0 = finalCreateStore(reducer) 105 // 输出: 106 // speaker was called with state {} and action { type: '@@redux/INIT' } 107 // speaker was called with state {} and action { type: '@@redux/PROBE_UNKNOWN_ACTION_s.b.4.z.a.x.a.j.o.r' } 108 // speaker was called with state {} and action { type: '@@redux/INIT' } 109 110 // 现在 store 的 middleware 已经准备好了,再来尝试分发我们的异步 action: 111 112 var asyncSayActionCreator_1 = function (message) { 113 return function (dispatch) { 114 setTimeout(function () { 115 console.log(new Date(), 'Dispatch action now:') 116 dispatch({ 117 type: 'SAY', 118 message 119 }) 120 }, 2000) 121 } 122 } 123 124 console.log("\n", new Date(), 'Running our async action creator:', "\n") 125 126 store_0.dispatch(asyncSayActionCreator_1('Hi')) 127 // 输出: 128 // Mon Aug 03 2015 00:01:20 GMT+0200 (CEST) Running our async action creator: 129 // Mon Aug 03 2015 00:01:22 GMT+0200 (CEST) 'Dispatch action now:' 130 // speaker was called with state {} and action { type: 'SAY', message: 'Hi' } 131 132 // 当我们调用异步 action creator 两秒之后,action 成功被分发出去。 133 134 // 你可能会好奇,一个中间件如何 log 出所有已分发的 action , 135 // 是这样: 136 137 function logMiddleware ({ dispatch, getState }) { 138 return function(next) { 139 return function (action) { 140 console.log('logMiddleware action received:', action) 141 return next(action) 142 } 143 } 144 } 145 146 // 同样的,下面是一个中间件,它会丢弃所有经过的 action(不是很实用, 147 // 但是如果加一些判断就能实现丢弃一些 action,放到一些 action 给下一个中间件): 148 function discardMiddleware ({ dispatch, getState }) { 149 return function(next) { 150 return function (action) { 151 console.log('discardMiddleware action received:', action) 152 } 153 } 154 } 155 156 // 通过使用 logMiddleware 或 discardMiddleware 试着修改上述的 finalCreateStore 调用 157 // 看看会发生什么... 158 // 比如,这样用: 159 // const finalCreateStore = applyMiddleware(discardMiddleware, thunkMiddleware)(createStore) 160 // 会让你的 action 永远无法到达 thunkMiddleware 和 reducer。 161 162 // 查看 http://rackt.org/redux/docs/introduction/Ecosystem.html 的中间件部分可以了解其他例子。 163 164 // 总结一下到目前为止我们所学的: 165 // 1) 我们知道怎样写 action 和 action creator 166 // 2) 我们知道怎样分发 action 167 // 3) 我们知道怎样使用中间件处理自定义 action,比如异步 action 168 169 // 对于 Flux 体系的完整闭环,我们还剩下唯一的一块就是如何订阅 state 的更新 170 // 并响应这些更新(比如重新渲染我们的组件) 171 172 // 所以我们怎么订阅 Redux store 的更新呢? 173 174 // 继续下一个教程: 10_state-subscriber.js