前言
- 上一篇说的dynamic,这一篇实现onAction和onStateChage。
利用onAction实现redux-logger
- 这个使用在第一篇就说过了,从第二篇loading的实现可以发现redux-logger使用的是onAction的钩子。而onAction其实是个中间件钩子。
- 所以我们需要把自己写的dva里把钩子加入中间件。
- 首先就是把前面写的钩子管理机里的hooks增加上onAction
const hooks = [
"onEffect",//增强effect
"extraReducers",//添加reducer
"onAction"//增加中间件
]
- 这样dva里面可以通过plugin.get获取到传来的onAction的中间件了。
- 然后在中间件位置加入:
let extraMiddleware = plugin.get('onAction')
let store = applyMiddleware(routerMiddleware(history), sagaMiddleware, ...extraMiddleware)(createStore)(reducer)
- 这样使用官方的logger就可以打印出日志了。
- 下面实现logger,其实就是写个redux中间件。
const logger = ({ dispatch, getState }) => next => action => {
let prevState = getState()
next(action)
let nextState = getState()
console.log('prev state', prevState);
console.log('action', action);
console.log('next state', nextState);
}
export default logger
-
中间件格式一模一样。调用next会给下一个中间件,走完里面的最后出来再拿值就是改调的state了,这也是洋葱模型。
-
我们可以再修改一下,变得和logger打印出来样式一样。
-
我们把样式添加上:
const logger = ({ dispatch, getState }) => next => action => {
let prevState = getState()
next(action)
let nextState = getState()
console.group(
`%caction %c${action.type} %c@${new Date().toLocaleTimeString()}`,
`color:grey;font-weight:lighter`,
`font-weight:bold`,
'color:grey;font-weight:lighter'
)
console.log('%cprev state', `color:#9E9E9E; font-weight:bold`, prevState);
console.log('%caction', `color:#03A9F4; font-weight:bold`, action);
console.log('%cnext state', `color:#4CAF50; font-weight:bold`, nextState);
console.groupEnd()
}
export default logger
- 这样打印的东西就跟原版一模一样了。
利用onStateChage持久化
- 这个是有插件的,第六篇说,先从最简单的实现开始讲,主要还是为了实现一个onStateChage的钩子。
- 首先是使用,先全部用原版的:
app.use({
onStateChange(state) {
localStorage.setItem('state', JSON.stringify(state))
}
})
- 这个就是做的onStateChage钩子,每当状态发生变更就会执行这个回调函数。这个函数里面写的就是把state存进loacalstorage。读取等会再说,先实现存入。
- onStateChange是个钩子,所以在plugin.js里加入这个钩子:
const hooks = [
"onEffect",//增强effect
"extraReducers",//添加reducer
"onAction",
"onStateChange"
]
- 然后,这里就需要使用store.subscribe了。在redux这篇文章里可以发现store.subscribe就是传入listener,然后当dispatch时候会调用listener,所以这里在拿到store后,加入这么一句:
let onStateChange = plugin.get('onStateChange')
store.subscribe(() => {
onStateChange.forEach(fn => fn(store.getState()))
})
- 这个函数就是当dispatch时会调用这个函数,让所有写在onStateChange上的函数全部执行。
- 有人可能会觉得这个跟加中间件有点像,什么时候加中间件,什么时候用subscribe。中间件对拦截的dispatch做操作,理论上也可以完成subscribe的操作,但是得注意中间件的顺序。中间件可进行的操作大于subscribe。redux那文章中可以看见subscribe的方法在dispatch中执行,调用dispatch就是执行subscribe。而中间件是可以把dispatch进行改写或在dispatch外面操作,所以subscribe在dispatch外面调用和里面调用都是可以的。
- 这样就完成了状态变更存入storage。有存也得有取,不然刷新就没了。
- 这里就要利用store的初始值了:
let app = dva({
initialState: localStorage.getItem('state') ? JSON.parse(localStorage.getItem('state')) : undefined
})
- 还需要在dva的index.js中写入它:
let store = applyMiddleware(routerMiddleware(history), sagaMiddleware, ...extraMiddleware)(createStore)(reducer, opts.initialState)
- 这样便完成了。store本来第二个参数是初始值,直接传给它就行了。一开始初学的时候感觉store的初始值好像没啥用,现在发现这个初始值真是特别好用。