创建支持异步的插件
主要 API
- createStore
- getState
- dispatch
- subscribe
- combineReducers
- applyMiddleware
- thunk // 新增
Core
const combineReducers = (reducers) => {
return (state, action) => {
const combineState = {}
const keys = Object.keys(reducers)
for (let key of keys) {
const partial = reducers[key](state === undefined ? state : state[key], action)
combineState[key] = partial
}
return combineState
}
}
const applyMiddleware = (...middlewares) => {
return (store) => {
let dispatch = store.dispatch
middlewares.forEach(middleware => {
dispatch = middleware(store)(dispatch)
})
return {
...store,
dispatch
}
}
}
const createStore = (reducer, applyMiddleware) => {
let state = reducer(undefined, {})
const listener = []
const store = {
getState: () => {
return state
},
subscribe: (fn) => {
listener.push(fn)
},
dispatch: (action) => {
state = reducer(state, action)
for (let fn of listener) {
fn && fn()
}
}
}
return applyMiddleware(store)
}
创建thunk插件
基本原理:
- 根据redux的概念,dispatch的时候,是传入一个action,那么它没法支持异步
- 我们让dispatch支持传入一个方法,在方法内部再去dispatch一个action,这样就实现了支持异步
const thunk = store => dispatch => action => {
if (typeof action === 'function') {
action(store.getState, dispatch)
}
return dispatch(action)
}
测试代码
const initCounterState = {
value: 0,
name: 'counter reducer'
}
const initTodoState = {
value: 0,
name: 'todo reducer'
}
const counterReducer = (state = initCounterState, action) => {
switch (action.type) {
case 'COUNTER/INCREMENT':
return {
...state,
value: state.value + (action.payload || 1)
}
case 'COUNTER/DECREMENT':
return {
...state,
value: state.value - (action.payload || 1)
}
default:
return {
...state
}
}
}
const todoReducer = (state = initTodoState, action) => {
switch (action.type) {
case 'TODO/INCREMENT':
return {
...state,
value: state.value + (action.payload || 1)
}
case 'TODO/DECREMENT':
return {
...state,
value: state.value - (action.payload || 1)
}
default:
return {
...state
}
}
}
const reducer = combineReducers({
counterReducer,
todoReducer,
})
const store = createStore(reducer, applyMiddleware(thunk))
store.subscribe(() => {
const state = store.getState()
})
store.dispatch({
type: 'TODO/INCREMENT',
payload: 10
})
store.dispatch({
type: 'COUNTER/INCREMENT',
payload: 5
})
store.dispatch({
type: 'TODO/DECREMENT',
payload: 2
})
// 测试异步操作案例一
store.dispatch(() => {
console.log('dispatching')
setTimeout(() => {
console.log('got new data')
const value = 100
store.dispatch({
type: 'TODO/INCREMENT',
payload: value
})
}, 2000)
})
// 给异步操作一个正当的名字
const addCounterByValue = (params) => {
return (getState, dispatch) => {
console.log(params, getState())
setTimeout(() => {
const value = 2000
dispatch({
type: 'COUNTER/INCREMENT',
payload: value
})
}, 3000)
}
}
store.dispatch(addCounterByValue('params'))