前言: 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
日常的使用中占到非常大的比重。
也是希望自己能了解当中究竟是怎么工作,面对问题时知道解决方案