Redux有3大核心,actions、store和reducer。Redux维护一个state,接受用户定义的reducer,用于根据action计算新的state,当用户触发action后,Redux调用reducer计算新的state,更新state,然后将新的state发布给订阅者。
下面讲一下redux的简单实现:createStore
这个API的实现。
先来看下createStore的使用。
const defaultState = {
// 开关默认关闭状态
isSwitchOn: false
};
function reducer(state = defaultState, action) {
switch (action.type) {
case 'open':
return {isSwitchOn: true};
case 'close':
return {isSwitchOn: false};
default:
return state;
}
}
const store = createStore(reducer);
const unsubscribe = store.subscribe(() => {
console.log('state', store.getState());
});
store.dispatch({type: 'open'});
unsubscribe();
我们看到createStore
接收一个方法reducer作为参数,返回一个对象store,store有3个方法
- getState,获取当前状态。
- subscribe,接收一个回调作为参数,订阅状态改变,它返回一个函数,用来解除订阅。
- dispatch,接收一个action对象作为参数,触发action之后会依据reducer计算新的状态,并通知订阅者。
根据上面的列举,我们用TypeScript写一个简单的redux
interface IStore {
subscribe: (cb: Function) => void,
getState: () => Object,
dispatch: (action: Object) => void
}
const createStore = (reducer: Function): IStore => {
let state = reducer();
const listeners = [];
const subscribe = cb => {
listeners.push(cb);
return () => {
const index = listeners.indexOf(cb);
listeners.splice(index, 1);
};
};
const getState = () => state;
const dispatch = action => {
state = reducer(action);
listeners.forEach(listener => listener());
};
return {
subscribe, getState, dispatch
};
};
但是在这个简单的实现中有几个问题没有考虑到:
- reducer应该是一个纯函数,但是用户如果在reducer中调用getState、subscribe、dispatch怎么办?
- 如果用户连续调用解绑函数怎么办?
- 如果用户在dispatch过程中,即在订阅者中做绑定和解绑的操作怎么办?
对于上面的问题,redux的解决方案是这样的:
- 设置标志位:
isDispatching
,用reducer计算新state时候置为true,在计算完再置为false。然后在getState、subscribe、dispatch方法中判断,如果isDispatching
为true,则抛出错误。 - 设置标志位:
isSubscribed
,它标识一个订阅者是否正在订阅中,订阅后置为true,解绑后置为false,如果用户连续调用解绑,redux判断isSubscribed
为false,就直接return了。 - redux设置了
currentListners
和nextListners
两个数组,这两个数组就是为了防止执行listners时候还添加和移除监听器。在添加和移除时候都新建一个区别于currentListners
的数组:nextListeners
,在nextListners
添加或删除listener,当dispatch时候再将currentListners
和nextListners
同步。
redux的createStore就是实现了上面提到的API并且完美解决了上面提到的问题。
阅读redux代码,可以理解的更深刻:redux-github、redux createStore源码。