# 完整的redux
# 退订
不能退订的订阅都是耍流氓!我们修改下store.subscribe方法,增加退订功能
function subscribe(listener) {
listeners.push(listener);
return function unsubscribe() {
const index = listeners.indexOf(listener);
listeners.splice(index, 1);
}
}
使用
const unsubscribe = store.subscribe(() => {
const state = store.getState();
console.log(state.counter.count);
});
// 退订
unsubscribe();
# 中间件拿到的store
现在的中间件拿到了完整的store,他甚至可以修改我们的subscribe方法,按照最小开放策略
,我们只用把getState给中间件就可以了!因为我们只允许你用getState方法
!
修改下applyMiddleware中给中间件传的store
// const chain = middlewares.map(middleware => middleware(store));
const simpleStore = { getState: store.getState };
const chain = middlewares.map(middleware => middleware(simpleStore));
# compose
我们的applyMiddleware中,把[A, B, C]转换成A(B(C(next))),是这样实现的
const chain = [A, B, C];
let dispatch = store.dispatch;
chain.reverse().map(middleware => {
dispatch = middleware(dispatch);
});
redux提供了一个compose方式,可以帮我们做这个事情
const chain = [A, B, C];
dispatch = compose(...chain)(store.dispatch);
看下它是如何实现的
export default function compose(...funcs) {
if (funcs.length === 1) {
return funcs[0];
}
return funcs.reduce((a, b) => (...args) => a(b(...args)));
}
当然compose函数对于新人来说可能比较难理解,你只需要知道他是做什么的就行啦!
# 省略initState
有时候我们创建store的时候不传initState,我们怎么用?
const store = createStore(reducer, {}, rewriteCreateStoreFunc);
redux允许我们这样写
const store = createStore(reducer, rewriteCreateStoreFunc);
我们仅需要改下createStore函数,如果第二个参数是一个object,我们认为它是initState,如果是function,我们就认为它是rewriteCreateStoreFunc。
function createStore(reducer, initState, rewriteCreateStoreFunc) {
if (typeof initState === 'function') {
rewriteCreateStoreFunc = initState;
initState = undefined;
}
...
}
# 2行代码的replaceReducer
reducer拆分后,和组件是一一对应的。我们就希望在做按需加载的时候,reducer也可以跟着组件在必要的时候再加载,然后用新的reducer替换老的reducer。
const createStore = function (reducer, initState) {
...
function replaceReducer(nextReducer) {
reducer = nextReducer;
// 刷新一遍 state 的值,新来的 reducer 把自己默认的状态放到 state 树上去
dispatch({ type: Symbol() });
}
...
return {
...
replaceReducer
}
}
我们来尝试使用下
const reducer = combineReducers({
counter: counterReducer
});
const store = createStore(reducer);
// 生成新的reducer
const nextReducer = combineReducers({
counter: counterReducer,
info: infoReducer
});
// replaceReducer
store.replaceReducer(nextReducer);
replaceReducer示例源码见demo-5 (opens new window)
# bindActionCreators
bindActionCreators我们很少很少用到,一般只有在react-redux的connect实现中用到。
它是做什么的?它通过闭包,把dispatch和actionCreator隐藏起来,让其他地方感知不到redux的存在。
我们通过普通的方式来隐藏dispatch和actionCreator试试,注意最后两行代码。
const reducer = combineReducers({
counter: counterReducer,
info: infoReducer
});
const store = createStore(reducer);
// 返回 action 的函数就叫 actionCreator
function increment() {
return {
type: 'INCREMENT'
}
}
function setName(name) {
return {
type: 'SET_NAME',
name: name
}
}
const actions = {
increment: function () {
return store.dispatch(increment.apply(this, arguments))
},
setName: function () {
return store.dispatch(setName.apply(this, arguments))
}
}
// 注意:我们可以把 actions 传到任何地方去
// 其他地方在实现自增的时候,根本不知道 dispatch,actionCreator等细节
actions.increment(); // 自增
actions.setName('九部威武'); // 修改 info.name
我眼睛一看,这个 actions 生成的时候,好多公共代码,提取一下
const actions = bindActionCreators({ increment, setName }, store.dispatch);
来看一下 bindActionCreators 的源码,超级简单(就是生成了刚才的 actions)
// 核心的代码在这里,通过闭包隐藏了 actionCreator 和 dispatch
function bindActionCreator(actionCreator, dispatch) {
return function () {
return dispatch(actionCreator.apply(this, arguments))
}
}
// actionCreators 必须是 function 或者 object
export default function bindActionCreators(actionCreators, dispatch) {
if (typeof actionCreators === 'function') {
return bindActionCreator(actionCreators, dispatch)
}
if (typeof actionCreators !== 'object' || actionCreators === null) {
throw new Error()
}
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
}
bindActionCreators 示例源码见 demo-8 (opens new window)
# 大功告成
完整的示例源码见demo-9 (opens new window),你可以和redux (opens new window)源码做一下对比,你会发现,我们已经有了redux所有的功能了。
当然,为了保证代码的理解性,我们少了一些参数的验证。比如createStore(reducer)
的参数reducer必须是function等等。
← 中间件middleware 最佳实践 →