-
Notifications
You must be signed in to change notification settings - Fork 4
/
StateStore.js
122 lines (103 loc) · 4.38 KB
/
StateStore.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
import { basicObject as verifyBasicObject, basicFunction as verifyBasicFunction } from 'source/common/verify'
import { objectMerge } from './Object'
import { arrayMatchPush, arrayMatchDelete } from './Array'
const createStateStore = (state) => {
verifyBasicObject(state, 'initialState should be basic Object')
let listenerList = []
const subscribe = (listener) => { listenerList = arrayMatchPush(listenerList, listener) }
const unsubscribe = (listener) => { listenerList = arrayMatchDelete(listenerList, listener) }
const getState = () => state
const setState = (nextState) => {
__DEV__ && verifyBasicObject(nextState, 'state should be basic Object')
nextState = objectMerge(state, nextState)
if (nextState === state) return state
const prevState = state
state = nextState
listenerList.forEach((listener) => listener(state, prevState))
return state
}
return { subscribe, unsubscribe, getState, setState }
}
// for basic use, no merge check & listener for speed
const createStateStoreLite = (state) => ({
getState: () => state,
setState: (nextState) => (state = { ...state, ...nextState })
})
// for redux-like use
const createStateStoreEnhanced = ({ initialState, enhancer, reducer }) => {
verifyBasicFunction(enhancer, 'enhancer function required')
verifyBasicFunction(reducer, 'reducer function required')
const { subscribe, unsubscribe, getState: getStoreState, setState: setStoreState } = createStateStore(initialState)
let dispatchingState = null
let isDispatching = false
let isReducing = false
const rootDispatch = (action) => {
dispatchingState = getStoreState()
isDispatching = true
enhancer(enhancerStore, action) // may trigger deeper dispatch
if (__DEV__ && !dispatchingState) throw new Error(`invalid dispatchingState from enhancer: ${JSON.stringify(dispatchingState)}`)
isDispatching = false
isReducing = true
const nextState = reducer(dispatchingState, action) // should not trigger dispatch
isReducing = false
dispatchingState = null
setStoreState(nextState) // may trigger deeper dispatch
}
const subDispatch = (action) => {
if (__DEV__ && isReducing) throw new Error(`unexpected reducer dispatch, action: ${JSON.stringify(action)}`)
enhancer(enhancerStore, action)
dispatchingState = reducer(dispatchingState, action)
__DEV__ && verifyBasicObject(dispatchingState, 'reducer should return Object state')
}
const getState = () => !isDispatching ? getStoreState() : dispatchingState
const dispatch = (action) => {
__DEV__ && verifyBasicObject(action, 'action should be basic Object')
return !isDispatching ? rootDispatch(action) : subDispatch(action)
}
const enhancerStore = { getState, dispatch }
return { subscribe, unsubscribe, getState, dispatch }
}
// for redux-like use, store should be createStateStore or createStateStoreEnhanced
const toReduxStore = (store) => {
const { subscribe: subscribeStore, unsubscribe } = store
verifyBasicFunction(subscribeStore, 'store.subscribe required')
verifyBasicFunction(unsubscribe, 'store.unsubscribe required')
const subscribe = (listener) => { // merge unsubscribe into subscribe
subscribeStore(listener)
return () => unsubscribe(listener)
}
return { ...store, subscribe }
}
const reducerFromMap = (reducerMap) => { // redux combineReducers
const keyList = Object.keys(reducerMap)
const keyListLength = keyList.length
return (state, action) => {
const nextState = {}
let isChanged = false
for (let index = 0; index < keyListLength; index++) {
const key = keyList[ index ]
const keyState = state[ key ]
const nextKeyState = reducerMap[ key ](keyState, action)
nextState[ key ] = nextKeyState
isChanged = isChanged || (nextKeyState !== keyState)
}
return isChanged ? nextState : state
}
}
const createEntryEnhancer = (entryMap) => (enhancerStore, action) => { // redux-entry like enhancer
const entryFunction = entryMap[ action.type ]
return entryFunction && entryFunction(enhancerStore, action)
}
const createStoreStateSyncReducer = (actionType, { getState, setState }) => (state, { type, payload }) => { // redux-entry like state sync reducer
type === actionType && setState(payload)
return getState()
}
export {
createStateStore,
createStateStoreLite,
createStateStoreEnhanced,
toReduxStore,
reducerFromMap,
createEntryEnhancer,
createStoreStateSyncReducer
}