基础方法
1、createStore
创建redux的store。支持两种形式
- createStore(reducer, enchancer),其中reducer为Reducer类型,enhancere为StoreEnhancer
- createStore(reducer, prelaodedState, enhancer),其中prelaodedState为PreloadedState类型
如果enhancer为函数,则直接返回enhancer(createStore)(reducer, preloadedState)
createStore创建时,没有指定enhancer时,会分发ActionTypes.INIT,返回store对象{dispatch, subscribe, getState, replaceReducer}
store对象主要有4个方法
dispatch(action: A)
根据当前状态以及行为执行currentReducer得到新的状态,触发监听器
function dispatch(action: A) { if (!isPlainObject(action)) { throw new Error( `Actions must be plain objects. Instead, the actual type was: '${kindOf( action )}'. You may need to add middleware to your store setup to handle dispatching other values, such as 'redux-thunk' to handle dispatching functions. See https://redux.js.org/tutorials/fundamentals/part-4-store#middleware and https://redux.js.org/tutorials/fundamentals/part-6-async-logic#using-the-redux-thunk-middleware for examples.` ) } if (typeof action.type === 'undefined') { throw new Error( 'Actions may not have an undefined "type" property. You may have misspelled an action type string constant.' ) } if (typeof action.type !== 'string') { throw new Error( `Action "type" property must be a string. Instead, the actual type was: '${kindOf( action.type )}'. Value was: '${String(action.type)}' (stringified)` ) } if (isDispatching) { throw new Error('Reducers may not dispatch actions.') } try { isDispatching = true currentState = currentReducer(currentState, action) } finally { isDispatching = false } const listeners = (currentListeners = nextListeners) listeners.forEach(listener => { listener() }) return action }subscribe(listener: () => void)
订阅监听者,同时返回取消注册
function subscribe(listener: () => void) { if (typeof listener !== 'function') { throw new Error( `Expected the listener to be a function. Instead, received: '${kindOf( listener )}'` ) } 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/store#subscribelistener for more details.' ) } let isSubscribed = true ensureCanMutateNextListeners() const listenerId = listenerIdCounter++ nextListeners.set(listenerId, 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/store#subscribelistener for more details.' ) } isSubscribed = false ensureCanMutateNextListeners() nextListeners.delete(listenerId) currentListeners = null } }getState(): S
获取当前的状态
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 }replaceReducer(nextReducer: Reducer<S, A>): void
替换reducer,重新计算状态
function replaceReducer(nextReducer: Reducer<S, A>): void { if (typeof nextReducer !== 'function') { throw new Error( `Expected the nextReducer to be a function. Instead, received: '${kindOf( nextReducer )}` ) } currentReducer = nextReducer as unknown as Reducer<S, A, PreloadedState> // This action has a similar 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) }2、applyMiddleware
应用redux的中间件,对store作功能增强。其返回StoreEnhancer。其实现为
function applyMiddleware(...middlewares) { return (createStore) => (reducer, preloadedState) => { const store = createStore(reducer, preloadedState) const middlewareAPI = {getState: store.getState, dispatch: (action, ...args) => dispatch(action, ...args)} const chain = middlewars.map(middleware => middleware(middlewareAPI)) dispatch = compose(..chain)(store.dispatch) return {...store, dispatch} } } 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)) ) }在对middlewars遍历得到chains,当中的每一项都是二级函数
compose是调用chains顺序是从右到左依次调用
3、combineReducers
合并Reducer,对多个reducer作统一处理,返回函数function combination(state:StateFromReducerMapObject<typeof reducers>, action:AnyAction):state
export type ReducersMapObject<S = any, A extends Action = AnyAction> = { [K in keyof S]: Reducer<S[K], A> }可以理解为S的key作为ReducersMapObject的key,value为Reducer的函数
export type StateFromReducersMapObject<M> = M extends ReducersMapObject ? { [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never } : neverStateFromReducersMapObject添加另一个泛型M约束,M如果继承ReducersMapObject,则走{ [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }的逻辑,否则就是never。
{ [P in keyof M]: M[P] extends Reducer<infer S, any> ? S : never }为对象,key来自M对象里面,也就是ReducersMapObject里面传入的S。key对应的value就是需要判断M[P]是否继承自Reducer,否则是never。
combineReducers函数实现为,finalReducerKeys为reducers的状态的key,finalReducers为key对应的reducer,combination函数遍历reducerKey,通过reducer计算得到当前key的状态nextStateForKey,统一放到状态对象nextState中,通过状态是否有改变返回nextState或者state。
function combination( state: StateFromReducersMapObject<typeof reducers> = {}, action: AnyAction ) { let hasChanged = false const nextState: StateFromReducersMapObject<typeof reducers> = {} for (let i = 0; i < finalReducerKeys.length; i++) { const key = finalReducerKeys[i] const reducer = finalReducers[key] const previousStateForKey = state[key] const nextStateForKey = reducer(previousStateForKey, action) nextState[key] = nextStateForKey hasChanged = hasChanged || nextStateForKey !== previousStateForKey } hasChanged = hasChanged || finalReducerKeys.length !== Object.keys(state).length return hasChanged ? nextState : state }4、compose
函数组合调用。其实现为
function compose(...funcs:Function[]) { if (funcs.length === 0) { return (arg) => arg } if (funcs.length === 1) { return funcs[0] } return funcs.reduce((a, b) => (...args) => a(b(...args))) }5、 Middleware
MiddlewareAPI接口包含两个方法:dispatch和getState
export interface MiddlewareAPI<D extends Dispatch = Dispatch, S = any> { dispatch: D getState: () => S }Middleware输入为MiddlewareAPI,输出为二级函数
其类型定义为,
export interface Middleware< _DispatchExt = {}, // TODO: see if this can be used in type definition somehow (can't be removed, as is used to get final dispatch type) S = any, D extends Dispatch = Dispatch > { ( api: MiddlewareAPI<D, S> ): (next: (action: unknown) => unknown) => (action: unknown) => unknown }redux开源中间件
- redux-thunk
- redux-promise
- redux-composable-fetch
- redux-saga
react-router-redux
将路由状态纳入redux的状态管理
import {browserHistory} from 'react-router'; import {syncHistoryWithStore} from 'react-router-redux'; import reducers from '<project-path>/reducers'; const store = createStore(reducers); const history = syncHistoryWithStore(browserHistory, store);用redux方式改变路由
import {browserHistory} from 'react-router'; import {routerMiddleware} from 'react-router-redux'; const middleware = routerMiddleware(browserHistory); const store = createStore(reducers, applyMiddleware(middleware));