应用模块之间须要访问共享数据,采用redux管理数据状态。全部数据保存在store tree中,用于维护数据状态。
“Redux 是 JavaScript 状态容器,提供可预测化的状态管理。”html
三大原则react
combineReducersgit
createStoregithub
store方法redux
数据流api
middlewares数组
实现redo&undo闭包
整个应用的 state 被储存在一棵 object tree 中,而且这个 object tree 只存在于惟一一个 store 中。app
唯一改变 state 的方法就是触发 action,action 是一个用于描述已发生事件的普通对象。经过 store.dispatch() 将 action 传到 store。action 内必须使用一个字符串类型的 type 字段来表示将要执行的动做异步
let action = { type: 'CHANGE_TEXT', text:'helloworld' }; store.dispatch(action);
能够经过Action 建立函数 生成 action 。
function changeText(text) { return { type: 'CHANGE_TEXT', text } } store.dispatch(changeText('helloworld'));
经过reducer改变state tree,要求reducer是纯函数,即只要传入参数相同,返回计算获得的下一个 state 就必定相同。没有特殊状况、没有反作用,没有 API 请求、没有变量修改,单纯执行计算。
function todoApp(state = initialState, action) { switch (action.type) { case 'CHANGE_TEXT': return Object.assign({}, state, { text: action.text }) default: return state } }
注意:
1)不要修改 state。
2)在 default 状况下返回旧的 state。
开发一个函数来作为主 reducer,它调用多个子 reducer 分别处理 state 中的一部分数据,而后再把这些数据合成一个大的单一对象。每一个 reducer 只负责管理全局 state 中它负责的一部分。每一个 reducer 的 state 参数都不一样,分别对应它管理的那部分 state 数据。
import { combineReducers } from 'redux' const initialUser = { name:"chen", age:10 }; const initialJob = { position:"engineer" }; const userReducer = (state = initialUser, action) => { let payload = action.payload; let type = action.type; switch (type) { case "CHANGE_NAME": state = Object.assign({},state,{ name : payload}); break; case "ADD_AGE": state = Object.assign({},state,{ age : state.age+1}); break; default: break; } return state; }; const jobReducer = (state = initialJob, action) => { let payload = action.payload; let type = action.type; switch (type) { case "CHANGE_POSITION": state = Object.assign({},state,{ position : payload}); break; default: break; } return state; }; const reducers = combineReducers({ userReducer, jobReducer }) export default reducers
经过createStore生成store tree。createStore() 的第二个参数是可选的, 用于设置 state 初始状态
const store = createStore( reducers );
提供 getState() 方法获取 state;
提供 dispatch(action) 方法更新 state;
经过 subscribe(listener) 注册监听器;
经过 subscribe(listener) 返回的函数注销监听器。
严格的单向数据流
数据流动方向
问题:没法进行异步或辅助操做。
提供了位于 action 被发起以后,到达 reducer 以前的扩展点。 能够用来进行日志记录、建立崩溃报告、调用异步接口或者路由。
如下是3个不一样功能的中间件,经过输出数组的方式将3个中间件输出模块
redux 提供了 applyMiddleware 这个 api 来加载 middleware
export default function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState, enhancer) => { const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) } chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose(...chain)(store.dispatch) return { ...store, dispatch } } }
const m1 = store => next => action => { let startState = store.getState(); console.log("m1 start"); console.log(startState.user.age); next(action); let endState = store.getState(); console.log("m1 end"); console.log(endState.user.age); }; const m2 = store => next => action => { let startState = store.getState(); console.log("m2 start"); console.log(startState.user.age); next(action); let endState = store.getState(); console.log("m2 end"); console.log(endState.user.age); }; const m3 = store => next => action => { let startState = store.getState(); console.log("m3 start"); console.log(startState.user.age); next(action); let endState = store.getState(); console.log("m3 end"); console.log(endState.user.age); }; const middlewares = [m1, m2, m3]; export default middlewares
import { createStore, applyMiddleware } from 'redux'; import reducers from './reducers'; import middlewares from './middlewares'; import reducers from './reducers'; import middlewares from './middlewares'; const defaultState = {}; const store = applyMiddleware(...middlewares)(createStore)(reducers, defaultState); window.store = store;
applyMiddleware 是一个多层柯里化(curry)的函数
经过applyMiddleware(...middlewares)能够将[m1,m2,m3]3个中间件串联起来,下面分析如下具体实现方式。
1)初始化store将dispatch指向store.dispatch
const store = createStore(reducer, preloadedState, enhancer) let dispatch = store.dispatch
2)用middlewareAPI封装store方法,middlewareAPI.dispatch方法最终指向store.dispatch。
let chain = [] const middlewareAPI = { getState: store.getState, dispatch: (action) => dispatch(action) }
3)经过将middlewareAPI传入每一个中间件[m1,m2,m3],返回chain,此时chain=[f1,f2,f3],且因为闭包,f1,f2,f3中的store都指向middlewareAPI ,最终指向store。这样的好处就是每一个f都能访问到同一个store。
chain = middlewares.map(middleware => middleware(middlewareAPI))
4)经过compose函数将[f1,f2,f3] 串联执行,最后dispatch被改写成了以下函数。
dispatch = f1(f2(f3(store.dispatch))); /* dispatch = (action)=>{ console.log("m1 start"); ((action) =>{ console.log("m2 start"); ((action) => { console.log("m3 start"); store.dispatch(action) console.log("m3 end"); })(action); console.log("m2 end"); })(action); console.log("m1 end"); } */
能够看到,此时f1’,f2’,f3’ 中的next指向中,只有最后一个f3’是指向store.dispatch,其他next指向前一个f'的输出。f1’,f2’,f3’全部store都指向middlewareAPI,最终getState和dispatch仍是指向store。经过这样的方式能够将中间件串联起来。
执行一次dispatch
store.dispatch({type:"ADD_AGE"})
输出结果以下:
中间件的串联并非简单依次执行,而是从middlewares数组的右边开始,依次将后一个中间件输出当成的函数(一个接收action的函数)做为前一个的next。
最后,通过采用middleware,咱们加入middleware来实现“非纯操做”,如请求异步接口,进队列,出队列,处理数据等。
https://zhuanlan.zhihu.com/p/...
https://github.com/reactjs/redux