應用模塊之間須要訪問共享數據,採用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