React系列 --- 簡單模擬語法(一)
React系列 --- Jsx, 合成事件與Refs(二)
React系列 --- virtualdom diff算法實現分析(三)
React系列 --- 從Mixin到HOC再到HOOKS(四)
React系列 --- createElement, ReactElement與Component部分源碼解析(五)
React系列 --- 從使用React瞭解Css的各類使用方案(六)
React系列 --- 從零構建狀態管理及Redux源碼解析(七)
React系列 --- 擴展狀態管理功能及Redux源碼解析(八)html
基本功能以後,咱們再回頭看看createStore.ts裏有什麼關鍵代碼實現功能的react
import $$observable from 'symbol-observable' import { Store, PreloadedState, StoreEnhancer, Dispatch, Observer, ExtendState } from './types/store' import { Action } from './types/actions' import { Reducer } from './types/reducers' import ActionTypes from './utils/actionTypes' import isPlainObject from './utils/isPlainObject'
頭部引入了symbol-observable
作響應式數據,其他都是一些類型聲明和工具函數git
/** * Creates a Redux store that holds the state tree. * The only way to change the data in the store is to call `dispatch()` on it. * * There should only be a single store in your app. To specify how different * parts of the state tree respond to actions, you may combine several reducers * into a single reducer function by using `combineReducers`. * * @param reducer A function that returns the next state tree, given * the current state tree and the action to handle. * * @param preloadedState The initial state. You may optionally specify it * to hydrate the state from the server in universal apps, or to restore a * previously serialized user session. * If you use `combineReducers` to produce the root reducer function, this must be * an object with the same shape as `combineReducers` keys. * * @param enhancer The store enhancer. You may optionally specify it * to enhance the store with third-party capabilities such as middleware, * time travel, persistence, etc. The only store enhancer that ships with Redux * is `applyMiddleware()`. * * @returns A Redux store that lets you read the state, dispatch actions * and subscribe to changes. */
函數註釋來看有三個入參github
參數 | 描述 |
---|---|
reducer | 給與當前state和action返回新的state |
preloadedState | 初始化state,能夠從服務器獲取或者恢復之前用戶序列化的緩存數據 |
enhancer | 能夠指定例如中間件的第三方功能加強 |
返回的store可讓你讀取state, 觸發actions,監聽變化算法
-------------省略部分代碼---------------- if ( (typeof preloadedState === 'function' && typeof enhancer === 'function') || (typeof enhancer === 'function' && typeof arguments[3] === 'function') ) { throw new Error( 'It looks like you are passing several store enhancers to ' + 'createStore(). This is not supported. Instead, compose them ' + 'together to a single function.' ) } if (typeof preloadedState === 'function' && typeof enhancer === 'undefined') { enhancer = preloadedState as StoreEnhancer<Ext, StateExt> preloadedState = undefined } if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState as PreloadedState< S >) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext } if (typeof reducer !== 'function') { throw new Error('Expected the reducer to be a function.') }
都是一些基本的判斷和報錯機制,也是咱們手寫代碼省略掉的一步,中間有關於enhancer部分的代碼能夠後面再講express
let currentReducer = reducer let currentState = preloadedState as S let currentListeners: (() => void)[] | null = [] let nextListeners = currentListeners let isDispatching = false /** * This makes a shallow copy of currentListeners so we can use * nextListeners as a temporary list while dispatching. * * This prevents any bugs around consumers calling * subscribe/unsubscribe in the middle of a dispatch. */ function ensureCanMutateNextListeners() { if (nextListeners === currentListeners) { nextListeners = currentListeners.slice() } } /** * Reads the state tree managed by the store. * * @returns The current state tree of your application. */ function getState(): S { if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return currentState as S }
開頭是基本的聲明變量,相比較咱們多了一個nextListeners
和isDispatching
redux
前者是做爲currentListeners
淺拷貝的臨時變量給分發階段使用的,這樣能夠避免在這過程當中會用subscribe/unsubscribe所致使的bugsegmentfault
後者是用來鎖定狀態,在dispatching的過程當中作對應邏輯api
/** * Adds a change listener. It will be called any time an action is dispatched, * and some part of the state tree may potentially have changed. You may then * call `getState()` to read the current state tree inside the callback. * * You may call `dispatch()` from a change listener, with the following * caveats: * * 1. The subscriptions are snapshotted just before every `dispatch()` call. * If you subscribe or unsubscribe while the listeners are being invoked, this * will not have any effect on the `dispatch()` that is currently in progress. * However, the next `dispatch()` call, whether nested or not, will use a more * recent snapshot of the subscription list. * * 2. The listener should not expect to see all state changes, as the state * might have been updated multiple times during a nested `dispatch()` before * the listener is called. It is, however, guaranteed that all subscribers * registered before the `dispatch()` started will be called with the latest * state by the time it exits. * * @param listener A callback to be invoked on every dispatch. * @returns A function to remove this change listener. */ function subscribe(listener: () => void) { if (typeof listener !== 'function') { throw new Error('Expected the listener to be a function.') } if (isDispatching) { throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.' ) } let isSubscribed = true ensureCanMutateNextListeners() nextListeners.push(listener) return function unsubscribe() { if (!isSubscribed) { return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' + 'See https://redux.js.org/api-reference/store#subscribelistener for more details.' ) } isSubscribed = false ensureCanMutateNextListeners() const index = nextListeners.indexOf(listener) nextListeners.splice(index, 1) currentListeners = null } }
比起咱們redux還作了幾層機制數組
isDispatching
狀態控制isSubscribed
控制移除事件狀態/** * Dispatches an action. It is the only way to trigger a state change. * * The `reducer` function, used to create the store, will be called with the * current state tree and the given `action`. Its return value will * be considered the **next** state of the tree, and the change listeners * will be notified. * * The base implementation only supports plain object actions. If you want to * dispatch a Promise, an Observable, a thunk, or something else, you need to * wrap your store creating function into the corresponding middleware. For * example, see the documentation for the `redux-thunk` package. Even the * middleware will eventually dispatch plain object actions using this method. * * @param action A plain object representing 「what changed」. It is * a good idea to keep actions serializable so you can record and replay user * sessions, or use the time travelling `redux-devtools`. An action must have * a `type` property which may not be `undefined`. It is a good idea to use * string constants for action types. * * @returns For convenience, the same action object you dispatched. * * Note that, if you use a custom middleware, it may wrap `dispatch()` to * return something else (for example, a Promise you can await). */ function dispatch(action: A) { if (!isPlainObject(action)) { throw new Error( 'Actions must be plain objects. ' + 'Use custom middleware for async actions.' ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. ' + 'Have you misspelled a constant?' ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } return action }
更加多的邏輯代碼
isDispatching
狀態控制/** * Replaces the reducer currently used by the store to calculate the state. * * You might need this if your app implements code splitting and you want to * load some of the reducers dynamically. You might also need this if you * implement a hot reloading mechanism for Redux. * * @param nextReducer The reducer for the store to use instead. * @returns The same store instance with a new reducer in place. */ function replaceReducer<NewState, NewActions extends A>( nextReducer: Reducer<NewState, NewActions> ): Store<ExtendState<NewState, StateExt>, NewActions, StateExt, Ext> & Ext { if (typeof nextReducer !== 'function') { throw new Error('Expected the nextReducer to be a function.') } // TODO: do this more elegantly ;((currentReducer as unknown) as Reducer< NewState, NewActions >) = nextReducer // This action has a similiar effect to ActionTypes.INIT. // Any reducers that existed in both the new and old rootReducer // will receive the previous state. This effectively populates // the new state tree with any relevant data from the old one. dispatch({ type: ActionTypes.REPLACE } as A) // change the type of the store by casting it to the new store return (store as unknown) as Store< ExtendState<NewState, StateExt>, NewActions, StateExt, Ext > & Ext }
reducers的替代方法,通常場景比較少用到,基本代碼很少
/** * Interoperability point for observable/reactive libraries. * @returns A minimal observable of state changes. * For more information, see the observable proposal: * https://github.com/tc39/proposal-observable */ function observable() { const outerSubscribe = subscribe return { /** * The minimal observable subscription method. * @param observer Any object that can be used as an observer. * The observer object should have a `next` method. * @returns An object with an `unsubscribe` method that can * be used to unsubscribe the observable from the store, and prevent further * emission of values from the observable. */ subscribe(observer: unknown) { if (typeof observer !== 'object' || observer === null) { throw new TypeError('Expected the observer to be an object.') } function observeState() { const observerAsObserver = observer as Observer<S> if (observerAsObserver.next) { observerAsObserver.next(getState()) } } observeState() const unsubscribe = outerSubscribe(observeState) return { unsubscribe } }, [$$observable]() { return this } } }
觀察者模式的實現庫作監聽事件
// When a store is created, an "INIT" action is dispatched so that every // reducer returns their initial state. This effectively populates // the initial state tree. dispatch({ type: ActionTypes.INIT } as A) const store = ({ dispatch: dispatch as Dispatch<A>, subscribe, getState, replaceReducer, [$$observable]: observable } as unknown) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext return store
方法的最後會觸發一個ActionTypes.INIT
的action作初始化數據,返回一個包含暴露的方法對象出去.
咱們再看看actionTypes.ts
源碼作了什麼
/** * These are private action types reserved by Redux. * For any unknown actions, you must return the current state. * If the current state is undefined, you must return the initial state. * Do not reference these action types directly in your code. */ const randomString = () => Math.random() .toString(36) .substring(7) .split('') .join('.') const ActionTypes = { INIT: `@@redux/INIT${randomString()}`, REPLACE: `@@redux/REPLACE${randomString()}`, PROBE_UNKNOWN_ACTION: () => `@@redux/PROBE_UNKNOWN_ACTION${randomString()}` } export default ActionTypes
不在於獲得什麼結果,只須要它是複雜難以跟開發定義的action重複就好了,爲了執行一次distapch獲取到初始的state.
學習完createStore.ts源碼以後咱們能夠將一些好的地方引入咱們的庫裏
nextListeners
充當臨時變量傳遞給其餘函數使用isDispatching
做爲狀態標記判斷流程dispatch
函數增長容錯機制,返回原樣action
isSubscribed
控制監聽事件解綁機制,從從新過濾賦值改爲根據索引值移除事件action
執行預觸發返回每一個reducer的初始數據function createStore (initStore = {}, reducer) { // 惟一數據源 let state = initStore // 監聽隊列 let listenList = [] // 監聽隊列淺拷貝 let nextListeners = listenList // 是否dispatch中 let isDispatching = false // 淺拷貝 function ensureCanMutateNextListeners () { if (nextListeners === listenList) { nextListeners = listenList.slice() } } // 惟一獲取數據函數 function getState () { // 輸出警告 if (isDispatching) { throw new Error( 'You may not call store.getState() while the reducer is executing. ' + 'The reducer has already received the state as an argument. ' + 'Pass it down from the top reducer instead of reading it from the store.' ) } return state } // 純函數來執行修改,只返回最新數據 const dispatch = (action) => { // 嚴格控制dispatch,不得中途再次發送 if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } // 增長意外防止操做 try { isDispatching = true state = reducer(state, action) } finally { isDispatching = false } // 獲取更改後的數據同時獲取最新隊列 const listeners = (listenList = nextListeners) // 替換成原始遍歷提升性能,遍歷觸發事件 for (let i = 0; i < listeners.length; i++) { const listener = listeners[i] listener() } // 爲了方便將action原樣返回 return action } // 添加監聽器, 同時返回解綁該事件的函數 const subscribe = (fn) => { if (isDispatching) { throw new Error( 'You may not call store.subscribe() while the reducer is executing. ' + 'If you would like to be notified after the store has been updated, subscribe from a ' + 'component and invoke store.getState() in the callback to access the latest state. ' ) } // 佔位標記 let isSubscribed = true // 每次添加監聽事件時淺拷貝最新隊列 ensureCanMutateNextListeners() nextListeners.push(fn) return function unsubscribe () { if (!isSubscribed) { return } if (isDispatching) { throw new Error( 'You may not unsubscribe from a store listener while the reducer is executing. ' ) } isSubscribed = false // 每次移除監聽事件時淺拷貝最新隊列 ensureCanMutateNextListeners() // 根據索引值刪除比filter過濾從新賦值效率高 const index = nextListeners.indexOf(fn) nextListeners.splice(index, 1) listenList = null } } // 默認觸發一次dispatch以獲取各個reduce的初始數據 dispatch({ type: `@@redux/INIT${Math.random() .toString(36) .substring(7) .split('') .join('.')}` }) return { getState, dispatch, subscribe } }
文章的完整代碼能夠直接查看demo6
createStore
函數還有一個入參enhancer
咱們以前沒實現,
React提供使用中間件的惟一方式是applyMiddleware
函數,咱們看一下怎麼介紹它的
Middleware 可讓你包裝 store 的 dispatch 方法來達到你想要的目的。同時, middleware 還擁有「可組合」這一關鍵特性。多個 middleware 能夠被組合到一塊兒使用,造成 middleware 鏈。其中,每一個 middleware 都不須要關心鏈中它先後的 middleware 的任何信息
import { createStore, applyMiddleware } from 'redux' import todos from './reducers' function logger({ getState }) { return (next) => (action) => { console.log('will dispatch', action) // 調用 middleware 鏈中下一個 middleware 的 dispatch。 let returnValue = next(action) console.log('state after dispatch', getState()) // 通常會是 action 自己,除非 // 後面的 middleware 修改了它。 return returnValue } } let store = createStore( todos, [ 'Use Redux' ], applyMiddleware(logger) ) store.dispatch({ type: 'ADD_TODO', text: 'Understand the middleware' }) // (將打印以下信息:) // will dispatch: { type: 'ADD_TODO', text: 'Understand the middleware' } // state after dispatch: [ 'Use Redux', 'Understand the middleware' ]
logger
是通用的中間件格式,這是一個三層嵌套函數,分別是{getState, dispatch}, next(實際上是下一個包裝後的中間件)和action入參,其實至關於
function middleware ({getState, dispatch}) { return (next) => { return (action) => { // dosomething // 調用 middleware 鏈中下一個 middleware 的 dispatch。 let returnValue = next(action) // dosomething // 通常會是 action 自己,除非 // 後面的 middleware 修改了它。 return returnValue } } }
知道這個基本規則以後咱們就能夠看看applyMiddleware
裏面作了什麼
咱們看一下先過一下源碼裏面作了些什麼
import compose from './compose' import { Middleware, MiddlewareAPI } from './types/middleware' import { AnyAction } from './types/actions' import { StoreEnhancer, StoreCreator, Dispatch } from './types/store' import { Reducer } from './types/reducers' /** * Creates a store enhancer that applies middleware to the dispatch method * of the Redux store. This is handy for a variety of tasks, such as expressing * asynchronous actions in a concise manner, or logging every action payload. * * See `redux-thunk` package as an example of the Redux middleware. * * Because middleware is potentially asynchronous, this should be the first * store enhancer in the composition chain. * * Note that each middleware will be given the `dispatch` and `getState` functions * as named arguments. * * @param middlewares The middleware chain to be applied. * @returns A store enhancer applying the middleware. * * @template Ext Dispatch signature added by a middleware. * @template S The type of the state supported by a middleware. */
總的來講就是建立一個應用程序的中間件去加強redux store的dispatch方法,對多種類的任務來講很是便利,例如以簡潔的方式表達異步流程或者輸出每一個action payload的日誌,而每一箇中間件都會拿到dispatch
和getState
入參
-------------省略部分代碼---------------- export default function applyMiddleware( ...middlewares: Middleware[] ): StoreEnhancer<any> { return (createStore: StoreCreator) => <S, A extends AnyAction>( reducer: Reducer<S, A>, ...args: any[] ) => { const store = createStore(reducer, ...args) let dispatch: Dispatch = () => { throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) } const middlewareAPI: MiddlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } const chain = middlewares.map(middleware => middleware(middlewareAPI)) dispatch = compose<typeof dispatch>(...chain)(store.dispatch) return { ...store, dispatch } } }
大體分析一下代碼裏作了什麼操做
createStore
函數reducer
和其餘入參store
dispatch
,拋出異常'不容許在構建中間件的時候dispatch,由於其餘中間件不會被應用到該次dispatch'middlewareAPI
對象,暴露出對應的方法,目的是讓每一個執行中間件都是同樣的入參條件middlewareAPI
以後的新函數數組dispatch
函數爲compose
以後的返回值store
實例的屬性方法和包裝後的新dispatch
方法上面有一個沒解析的compose
函數,源碼以下
/** * Composes single-argument functions from right to left. The rightmost * function can take multiple arguments as it provides the signature for the * resulting composite function. * * @param funcs The functions to compose. * @returns A function obtained by composing the argument functions from right * to left. For example, `compose(f, g, h)` is identical to doing * `(...args) => f(g(h(...args)))`. */ -------------省略部分代碼---------------- export default function compose(...funcs: Function[]) { if (funcs.length === 0) { // infer the argument type so it is usable in inference down the line return <T>(arg: T) => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args: any) => a(b(...args))) }
總的來講,除了類型判斷,實際代碼只有一個reduce
的應用...,這裏能夠知道每一箇中間件是有順序關係的,因此應用的時候須要注意一下.
applyMiddleware
的相關源碼已通過了一遍,剩下咱們回顧一下在createStore
裏是怎麼處理相關邏輯的,放心,真的很少
-------------省略部分代碼---------------- if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } return enhancer(createStore)(reducer, preloadedState as PreloadedState< S >) as Store<ExtendState<S, StateExt>, A, StateExt, Ext> & Ext } -------------省略部分代碼----------------
檢查到傳入enhancer
的時候直接中斷流程,返回執行結果,
咱們再從新梳理一下流程:
調用方式
createStore(reducer, preloadedState, applyMiddleware(f1, f2, ...fn))
在createStore裏若是檢測到enhancer入參會
return enhancer(createStore)(reducer, preloadedState) 至關於 return applyMiddleware(f1, f2, ...fn)(createStore)(reducer, preloadedState)
在applyMiddleware源碼可得知
// 初始化一個store let store = createStore(reducer, preloadedState); // 每一箇中間件拿到的入參 const middlewareAPI: MiddlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } // 遍歷的中間件大概流程這樣子 middlewares = [ f1(middlewareAPI) => s1(next) => t1(...arg) f2(middlewareAPI) => s2(next) => t2(...arg) fn(middlewareAPI) => sn(next) => tn(...arg) ] // chain獲得的數組就長這樣子 const chain = [ s1(next) => t1(...arg) s2(next) => t2(...arg) sn(next) => tn(...arg) ] // compose通過reduce方法包裝返回 const composeFn = s1((s2(sn(next) => tn(...arg))()) => t2(...arg))()) => t1(...arg) // 最終返回的dispatch方法 dispatch = (composeFn)(store.dispatch) // 總體流程 const applyMiddleware = (中間件數組) => (createStore) => (reducer, preloadedState) => {...store, dispatch: compose(...chain)(store.dispatch)}
這時候再回到中間件的通用代碼能夠知道
function middleware ({getState, dispatch}) { return (next) => { return (action) => { // dosomething // 調用 middleware 鏈中下一個 middleware 的 dispatch。 let returnValue = next(action) // dosomething // 通常會是 action 自己,除非 // 後面的 middleware 修改了它。 return returnValue } } }
階段一: 接收相同的初始{getState, dispatch}
階段二: 接收通過下一個中間件包裝後的dispatch調用
階段三: 接收action處理某些邏輯以後原樣返回,通常不應修改action
效果: dispatch一個action,會用倒序的方式逐一通過每一箇中間件的流程造成鏈式調用,而且先後通常不須要關心作些什麼操做.
咱們既然已經知道了它的實現思路,接下來就能夠簡單封裝一個了
function compose (...funcs) { if (funcs.length === 0) { // infer the argument type so it is usable in inference down the line return arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }
// 接收中間件數組 function applyMiddleware (...middlewares) { // 接收createStore函數和reducer和其餘參數 return (createStore) => (reducer, ...args) => { // 這就是原始的實例化store,因此applyMiddleware方法其實就是圍繞在原始store的基礎上添加功能 const store = createStore(reducer, ...args) // 先初始化dispatch方法佔位,可是此時執行會拋出異常 let dispatch = () => { throw new Error( 'Dispatching while constructing your middleware is not allowed. ' + 'Other middleware would not be applied to this dispatch.' ) } /** * 構建中間件第一層運行的入參對象, 保證每一箇中間件都是同樣的參數條件,因此上面的拋出異常也是如此 * applyMiddleware([ f1(middlewareAPI) => s1(next) => t1(...arg) fn(middlewareAPI) => sn(next) => tn(...arg) * ]) * */ const middlewareAPI = { getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args) } // 遍歷運行每一箇中間件返回新的數組 // chain = [s1(next) => t1(...arg), ...sn(next) => tn(...arg)] const chain = middlewares.map((middleware) => middleware(middlewareAPI)) /* 返回加強功能後的dispatch方法 dispatch = (s1(sn(next) => tn(...arg))()) => t1(...arg))(store.dispatch) */ dispatch = compose(...chain)(store.dispatch) // 替代原始的store對象 return { ...store, dispatch } } }
由於新增了加強功能,因此咱們也要把createStore
修改一下,按照源碼對應一下
由於參數裏只有reducer
是必選,其餘二者都是可選,因此咱們還要把入參順序也替換一下
function createStore (reducer, initStore = {}, enhancer) { // 處理一下參數問題 if (typeof initStore === 'function' && typeof enhancer === 'undefined') { enhancer = initStore initStore = undefined } // 劫持enhancer if (typeof enhancer !== 'undefined') { if (typeof enhancer !== 'function') { throw new Error('Expected the enhancer to be a function.') } // 返回包裝後的store return enhancer(createStore)(reducer, initStore) } -------------省略部分代碼---------------- return { getState, dispatch, subscribe } }
最後只剩執行函數
// 初始數據 const initStore = { arNum: 0, mdNum: 1 } // 日誌中間件 function logger ({ getState }) { return (next) => (action) => { console.log('will dispatch', action) // 調用 middleware 鏈中下一個 middleware 的 dispatch。 let returnValue = next(action) console.log('state after dispatch', getState()) // 通常會是 action 自己,除非 // 後面的 middleware 修改了它。 return returnValue } } // 實例化store let store = createStore(reducers, initStore, applyMiddleware(logger))
如今在index.html引入新的依賴執行能夠發現也能正常輸出日誌了.
文章的完整代碼能夠直接查看demo7
其實上面就已經算是完成了一個簡單的狀態管理器了,可是咱們從Redux的API裏其實可以看到還有一個方法是咱們還沒了解過的,就大概說說
把一個 value 爲不一樣 action creator 的對象,轉成擁有同名 key 的對象。同時使用 dispatch 對每一個 action creator 進行包裝,以即可以直接調用它們。唯一會使用到 bindActionCreators 的場景是當你須要把 action creator 往下傳到一個組件上,卻不想讓這個組件覺察到 Redux 的存在,並且不但願把 dispatch 或 Redux store 傳給它。
至於什麼情景會遇到須要使用
你或許要問:爲何不直接把 action creator 綁定到 store 實例上,就像傳統的 Flux 那樣?問題在於,這對於須要在服務端進行渲染的同構應用會有問題。多數狀況下,你的每一個請求都須要一個獨立的 store 實例,這樣你能夠爲它們提供不一樣的數據,可是在定義的時候綁定 action creator,你就只能使用一個惟一的 store 實例來對應全部請求了。
省略掉類型判斷後的源碼
import { Dispatch } from './types/store' import { AnyAction, ActionCreator, ActionCreatorsMapObject } from './types/actions' function bindActionCreator<A extends AnyAction = AnyAction>( actionCreator: ActionCreator<A>, dispatch: Dispatch ) { return function(this: any, ...args: any[]) { return dispatch(actionCreator.apply(this, args)) } } /** * Turns an object whose values are action creators, into an object with the * same keys, but with every function wrapped into a `dispatch` call so they * may be invoked directly. This is just a convenience method, as you can call * `store.dispatch(MyActionCreators.doSomething())` yourself just fine. * * For convenience, you can also pass an action creator as the first argument, * and get a dispatch wrapped function in return. * * @param actionCreators An object whose values are action * creator functions. One handy way to obtain it is to use ES6 `import * as` * syntax. You may also pass a single function. * * @param dispatch The `dispatch` function available on your Redux * store. * * @returns The object mimicking the original object, but with * every action creator wrapped into the `dispatch` call. If you passed a * function as `actionCreators`, the return value will also be a single * function. */ -------------省略部分代碼---------------- export default function bindActionCreators( actionCreators: ActionCreator<any> | ActionCreatorsMapObject, dispatch: Dispatch ) { if (typeof actionCreators === 'function') { return bindActionCreator(actionCreators, dispatch) } if (typeof actionCreators !== 'object' || actionCreators === null) { throw new Error( `bindActionCreators expected an object or a function, instead received ${ actionCreators === null ? 'null' : typeof actionCreators }. ` + `Did you write "import ActionCreators from" instead of "import * as ActionCreators from"?` ) } const boundActionCreators: ActionCreatorsMapObject = {} for (const key in actionCreators) { const actionCreator = actionCreators[key] if (typeof actionCreator === 'function') { boundActionCreators[key] = bindActionCreator(actionCreator, dispatch) } } return boundActionCreators }
bindActionCreator
返回綁定this指向的新函數
bindActionCreators
作了三件事:
actionCreators
是函數,直接返回調用bindActionCreator
actionCreators
非對象非null拋出異常actionCreators
是可迭代對象,返回遍歷調用bindActionCreator
包裝後的對象由於咱們的demo不須要用到,就不必實現了,你們知道原理便可