前言: createStore 是redux 创建 store 的方法,通过传入 reducer(数据处理函数),initialState(预设数据),enhancer(中间件,如时间穿梭等)返回一个store实例,具有getState,subscribe,dispatch,replaceReducer。接下将逐一分析各函数作用
给出一个最小化example
import { createStore } from 'redux'
function counterReducer (state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
let store = createStore(counterReducer)
store.subscribe(() => console.log(store.getState()))
store.dispatch({ type: 'counter/incremented' })
// {value: 1}
store.dispatch({ type: 'counter/incremented' })
// {value: 2}
store.dispatch({ type: 'counter/decremented' })
// {value: 1}
createStore
let store = createStore(counterReducer)
从实例可以看出,createStore中真正需要传递的仅仅是reducer这个处理函数,initialState 与 enhance都是可选。
export default function createStore(reducer) {
// xxxx
const store = {
dispatch: dispatch
subscribe,
getState,
replaceReducer,
[$$observable]: observable
}
return store
}
并且返回的store 中 是一个具有 dispatch subscribe getState replaceReducer 的对象
并且值得注意的是,createStore最后会调用dispatch({ type: ActionTypes.INIT } as A)
来获取初始的state值。
dispatch
store.dispatch({ type: 'counter/incremented' })
dispatch是redux 中唯一合法修改state的方法,通过调用dispatch 并且传入action来调用, 通常来说action是一个具有type 与 payload两个属性的对象
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 (isDispatching) {
throw new Error('Reducers may not dispatch actions.')
}
在dispatch中 首先会做一些错误判断,
!isPlainObject(action)判断action是否是是一个原生的对象字面量 或者是一个new Object()对象action对象中必须有type属性- 是否处于
dispatch处理过程中
try {
isDispatching = true
currentState = currentReducer(currentState, action)
} finally {
isDispatching = false
}
- 判断完后就是执行Reducer。更新最新的state
const listeners = (currentListeners = nextListeners)
for (let i = 0; i < listeners.length; i++) {
const listener = listeners[i]
listener()
}
再逐一通知所有订阅对象更新数据
return action
最后是返回action 对象
- 值得注意的是
dispatch过程中是 依次调用dispatch通过isDispatching来控制
subscribe
subscribe 是在组件内订阅store的更新,每当有dispatch 更新state数据后 就会触发。
可以在subscribe 中获取最新的state数据。并且subscribe 会返回一个 unsubscribe函数
在不再需要监听是的时候,调用unsubscribe 可以 清除监听函数
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.'
)
}
- 同样
subscribe开始也是会判断subscribe传入的必须是一个监听函数- 在
dispatching中不能添加监听对象
let isSubscribed = true
ensureCanMutateNextListeners()
nextListeners.push(listener)
-
ensureCanMutateNextListeners是对listener list 的浅拷贝store.subscribe(() => { console.log('outer'); store.subscribe(() => { console.log('inner'); }) })这里在
subscribe中又嵌套了subscribe。其中嵌套的subscribe只会在第二次store更新后,才会调用。因为 新添的
listener是加入到nextListeners中,不是加入到currentListeners中 -
有新添的
listener对象都是加入到nextListeners中。
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()
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
currentListeners = null
}
subscribe会返回一个unsubscribe 函数,用这个函数来终止订阅。
getState
getState是获取store中数据的方法,原理也比较简单,就是利用闭包来保存state,需要的时候直接调用即可取走
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 currentState
}
- 同样在
dispatching过程中不能获取state,为了防止dispatch过程中state修改
replaceReducer
这个也比较好理解,就是可以中途更换reducer 函数
function counterReducer (state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 1 }
case 'counter/decremented':
return { value: state.value - 1 }
default:
return state
}
}
function counterReducer1 (state = { value: 0 }, action) {
switch (action.type) {
case 'counter/incremented':
return { value: state.value + 10 }
case 'counter/decremented':
return { value: state.value - 10 }
default:
return state
}
}
const store = createStore(counterReducer)
store.replaceReducer(counterReducer1)
这样store 的 reducer 即更换为 counterReducer1
小结
createStore 通过传入的 reducer 来创建一个具有
dispatch: 唯一合法修改state数据的方法getState: 获取state数据方法subscribe: 订阅state的改变replaceReducer: 替换当前正在使用的reducer
相对于redux 中间件与action 来说,这一小节在redux 日常的使用中占到非常大的比重。
也是希望自己能了解当中究竟是怎么工作,面对问题时知道解决方案