redux 闲谈
原由: 在与涂鸦智能一个web工程师交换历程当中,他讯问我dispatch一个action,是怎样和reducer 绑定的,dispatch(actionA)只会触发reducerA却不会去触发reducerB.
redux 数据流程
redux 遵照严厉的单向数据流,以React为比方下图:
(网图,侵删)
- 经由历程用户在ViewUI 举行一个dispatch(action);
- Store内部自动经由历程以下情势Reducer(prevState, action)挪用
- Reducer返回新的State(newState), state变化后挪用Store上的监听器(store.subscribe(listener))
- 在listener内部能够经由历程
store.getState()
体式格局获得最新的state举行数据操纵
初始化
redux 的 Store
初始化经由历程 createStore
要领来举行初始化
const store = createStore(combineReducers, prevState, compose(applyMiddleware(...middleware)))
-
combineReducers
兼并后的reducer,reducer 情势以下
function authReducer(state, action) {
switch(action.type) {
case 'login':
return { ...state, isLogin: true }
default:
return {...state}
}
}
function userReducer(state, action) {
// ...如上
}
经由历程运用combineReducers({ authReducer, userReducer }) 返回一个reducers
-
prevState
则为reducer 中state 的初始化默认值,这里默认值为全部状况树的默认值 -
middleware
则为redux 中间件, 加强redux 功用
该部份初始化流程阶段,在下面applyMiddleware 会再次挪用
combineReducers 兼并 reducer
在上面说到我们能够存在多个reducer,也能够分模块来处置惩罚差别的状况题目,这里就须要兼并差别模块的reducer,完成代码:
export default function combineReducers(reducers) {
const reducerKeys = Object.keys(reducers)
const finalReducers = {}
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i]
// 其他代码...
if (typeof reducers[key] === 'function') {
finalReducers[key] = reducers[key] // ①
}
}
const finalReducerKeys = Object.keys(finalReducers)
// 其他代码...
return function combination(state = {}, action) { // ②
// 其他代码...
let hasChanged = false
const nextState = {}
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) // ③
if (typeof nextStateForKey === 'undefined') {
const errorMessage = getUndefinedStateErrorMessage(key, action)
throw new Error(errorMessage)
}
nextState[key] = nextStateForKey
hasChanged = hasChanged || nextStateForKey !== previousStateForKey
}
return hasChanged ? nextState : state
}
}
传入一个reducers = { authReducer, userReducer }, 很明显对reducers 举行了对象遍历,在①这个位置举行了单个reducer函数的拷贝,在②这个位置redux 内部本身创建了一个reducer函数为combination
, 在是③这个位置,举行了开发者定义的reducer定义,也就是说dispatch(action) -> combination(state, action) -> customReducer(state, action), 在轮回内部每次猎取对应的module 的state值作为 previousStateForKey
, 传入用户的reducer中,所以用户dispatch的 action中type是和reducer对应的位置在于用户本身的推断
compose 和 applyMiddleware
compose
compose 函数异常简短,代码以下:
export default function compose(...funcs) {
if (funcs.length === 0) {
return arg => arg
}
if (funcs.length === 1) {
return funcs[0]
}
return funcs.reduce((a, b) => (...args) => a(b(...args)))
}
传入一个不定量函数作为参数,重要作为函数从一个数组情势比方[login, fetchToken, fetchUser]如许的函数传入,获得则是fetchUser(fetchToken(login(…args))) 如许的情势, 将函数组合起来,并从右到左,而且最右边函数能够接收多个参数
示例仅仅为了说明,不在现实营业中涌现
applyMiddleware
依据函数字面意义该函数为运用redux 中间件,中心代码以下:
export default function applyMiddleware(...middlewares) {
return createStore => (...args) => { // 这里createStore 经由历程初始化时刻传入
const store = createStore(...args)
let dispatch = () => {
throw new Error(
'Dispatching while constructing your middleware is not allowed. ' +
'Other middleware would not be applied to this dispatch.'
)
}
const middlewareAPI = {
getState: store.getState,
dispatch: (...args) => dispatch(...args)
}
const chain = middlewares.map(middleware => middleware(middlewareAPI)) // ①
dispatch = compose(...chain)(store.dispatch) // ②
return {
...store,
dispatch
}
}
}
示例logger
const logger = store => next => action => { // example-①
console.log('dispatching', action)
let result = next(action)
console.log('next state', store.getState())
return result
}
// usage: applyMiddleware(logger)
这里applyMiddleware 接收不定量的redux中间件,我们就来诠释一下example-①
这里说明是哪里来的。
在 applyMiddleware
源码中,传入middlewares后,在 ① 的位置就好了第一个次的中间件挪用传入middlewareAPI,分别为getState
和 dispatch
两个要领这里对应 example-①
中的 store,在 ② 的位置举行了上文说的compose
挪用,把一切的中间件举行了组合,从右到左的挪用,如今传入dispatch 要领,这里要领对应 example-①
中的 next
,在上文中说到 compose
对 函数举行了组合,我们这里将store.dispatch 传入当做参数,返回一个新的函数等价于我们在ViewUI 中dispatch的时刻实在运用的 compose(...chain)(store.dispatch)(action)
如许的体式格局,所以在 example-①
中action 是开发者的action。运用中间件后的store.dispatch也是经由历程中间件包装后的dispatch。在末了 applyMiddleware
把dispatch 返回。
这里有点艰涩难明在于compose(…chain)(store.dispatch)(action), 这里能够如许明白,每次dispatch的时刻,中间件都是会实行一次,传入递次是[logger, crashReport], 实行递次为 crashReport -> logger, 自右向左实行。在每一个中间件实行历程当中都须要返回next(action) 将当前action 继承通报下去
其他参考: redux applyMiddleware 剖析
dispatch(action)
下面说到dispatch,这是我们经经常使用的,以下代码:
function dispatch(action) {
// 其他代码
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
作用, isDispatching 实行reducer 推断
这里这个参数处理状况以下:
var store = createStore((state={}, action) => {
if (something) {
store.dispatch({type: 'ANOTHER_ACTION'})
}
return state
})
继承下面来讲在 ① 的位置实行 currentReducer, 这里reducer 为我们经由历程 createStore
传入combineReducers, 把对应的currentState 和 action传入, currentState 也是在初始阶段传入的 preloadState。在 ② 的位置则举行触发监听器, 监听器设置则在 store.subscribe
中增添。
如今来诠释,action 和 reducer 对应关联
我当时回复的是: action.type 对应 reducer 的函数称号,假如一致的话就会实行。呵呵,谁人涂鸦大工程师有题目了怎样绑定的呢,怎样确定是reducerA 而不是B。呵呵呵呵呵。NB
后续
还问我 redux-saga、redux-thunk 异步处置惩罚计划,我提了一下redux-thunk,后续继承更新。
PS:真应当提拔全部沟通流程质量
1.参考地点:
https://redux.js.org