在介绍 createStore 方法原理之前,先回顾一下 redux 的使用过程,然后再根据使用的方式编写源码。
import { createStore } from 'redux'
import reducer from './reducer'
const store = createStore(reducer)
export default store
使用 redux 通过 createStore 创建容器,需要传入一个函数 reducer 称为处理器,这个函数定义怎样修改容器里面的状态。
const initState = { number: 0 }
function reducer(state = initState, action) {
switch (action.type) {
case 'INCREMENT':
return { number: state.number + 1 }
case 'DECREMENT':
return { number: state.number - 1 }
default:
return state
}
}
export default reducer
处理器函数第一个参数是要管理的状态,第二个参数是动作对象,那么接下来我们开始定义这个动作对象。
const actions = {
increment () {
return {type: 'INCREMENT'}
},
decrement () {
return {type: 'DECREMENT'}
}
}
export default actions
actions 可以写成对象和函数的形式,如果写成对象,那么对象中的每一项必须是函数。函数的返回值是对象,而且对象必须要有type属性。关于actions为什么要这样写了,目的是可以提供给 bindActionCreators 函数使用。
function bindActionCreator(actionCreator, dispatch) {
return (...args) => dispatch(actionCreator(...args))
}
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error('actionCreators是对象或函数')
}
const keys = Object.keys(actionCreators)
const boundActionCreators = {}
for (let i = 0; i < keys.length; i++) {
const key = keys[i]
const actionCreator = actionCreators[key]
if (typeof actionCreator === 'function') {
boundActionCreators[key] = bindActionCreator(actionCreator, dispatch)
}
}
return boundActionCreators
}
现在一切准备就绪,在组件中开始使用管理的状态,需要导入我们创建的 store 容器和定义的 actions 动作,这里并没有使用 react-redux,目的是为了演示 redux 的原始方法用法,为了更好的理解状态的更改的过程。
import store from './store'
import actions from './store/actions'
class App extends Component {
constructor(props) {
super(props);
this.state = {
number: 0
}
}
componentDidMount() {
this.unsubscribe = store.subscribe(() => {
this.setState({number: store.getState().number})
})
}
componentWillUnmount () {
this.unsubscribe()
}
handleClick = () => {
store.dispatch(actions.increment())
}
render() {
return (
<div>
<p>{this.state.number}</p>
<button onClick={this.handleClick}>+</button>
</div>
);
}
}
上面代码就是一个计数器组件,通过点击加号,会触发一个点击事件,点击事件会调用 dispatch 方法,传入参数是actions定义的动作类型,而且通过生命周期在组件挂载完成的在 subscribe 订阅了更新状态的方法,这样每次才能触发视图更新。
现在已经把 redux 的基本使用介绍完成,下面是实现内部的方法。
export const ActionTypes = {
INIT: '@@redux/INIT'
}
export default function createStore(reducer, preloadedState) {
let currentReducer = reducer
let currentState = preloadedState
let currentListeners = []
let nextListeners = currentListeners
let isDispatching = false
function getState() {
return currentState
}
function subscribe(listener) {
let isSubscribed = true
nextListeners.push(listener)
return function unsubscribe() {
if (!isSubscribed) {
return
}
isSubscribed = false
const index = nextListeners.indexOf(listener)
nextListeners.splice(index, 1)
}
}
function dispatch(action) {
if (isDispatching) {
throw new Error('reducer函数中不能调用dispatch')
}
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
}
dispatch({ type: ActionTypes.INIT })
return {
dispatch,
subscribe,
getState
}
}
currentState 是容器的状态,在容器的创建时指定第二个参是默认状态,每次调用 dispatch 会触发更改状态的处理器函数 currentReducer 得到新的状态,然后会触发订阅更新视图的方法 listener 函数。