2
ModuleConfiguration, ModuleCreator, ModuleCreatorsMap,
6
import { copy, isType, noop, types } from './helpers/base'
7
import { convertToStandardModule, isPureModule } from './helpers/module'
8
import { ActionModifier, Modifier, ModifiersWorker, ModuleModifier } from './ModifiersWorker'
9
import { StateWorker } from './StateWorker'
10
import { StonexModule } from './StonexModule'
11
import { createStoreBinder } from './StoreBinder'
14
* @typedef {Object} MP - Map of Stonex Modules class references
17
export declare interface Store<MP> {
18
modules: StonexModules<MP>
19
getState: <State>(moduleName: string) => State
22
changes: ((() => Partial<State>) | Partial<State>), callback?: (state: State) => any
24
resetState: (moduleName: string, callback?: (state: any) => any) => void
25
connectModule: <State> (
27
data: ModuleCreator<State, any>
28
) => StonexModule<State>
29
createStateSnapshot: () => StateSnapshot<MP>,
33
export declare interface StoreConfiguration<MP> {
34
stateWorker?: new (...args: any[]) => StateWorker,
35
modifiers?: Array<Modifier<MP>>
39
* Creates a new stonex store.
40
* Combining all your stonex modules together and allows to use them in your application.
43
* @implements {Store<MP>}
47
* import SomeModule from './SomeModule'
49
* const store = new StonexStore({
50
* someModule: SomeModule
53
* store.modules.someModule.doSomething()
55
* store.createStateSnapshot()
57
class StonexStore<MP> implements Store<MP> {
60
* Creates snapshot of state
62
* @param {MP} modules - Map with keys where key it is name of module and value it is class reference or object
65
* @memberof StonexStore
67
* @returns {StateSnapshot<MP>}
69
public static createStateSnapshot = <MP>(modules: MP): StateSnapshot<MP> =>
70
Object.keys(modules).reduce((state, name) => {
71
state[name] = copy(modules[name].state)
73
}, {}) as StateSnapshot<MP>
76
* Unique identificator of store.
77
* Usings inside Stonex Store. Don't change it!
80
* @memberof StonexStore
82
public storeId: number = Math.round(Math.random() * Number.MAX_SAFE_INTEGER - Date.now())
85
* Map of the Stonex modules
87
* @type {StonexModules<MP>}
88
* @memberof StonexStore
90
public modules: StonexModules<MP> = {} as StonexModules<MP>
93
* Set of methods which needed to work with module's state (updating, initializing, etc)
95
* Its can be overriden via StonexStore constructor:
97
* new StonexStore(modules, { stateWorker: OwnStateWorker })
100
* @type {StateWorker}
101
* @memberof StonexStore
103
private stateWorker: StateWorker
106
* Creates an instance of StonexStore.
108
* @param {Partial<ModuleCreatorsMap<MP>>} modulesMap
109
* @param {StoreConfiguration<MP>} storeConfiguration - have keys 'stateWorker', 'modifiers'
110
* @memberof StonexStore
113
modulesMap: Partial<ModuleCreatorsMap<MP>>,
114
{ stateWorker = StateWorker, modifiers }: StoreConfiguration<MP> = {}
116
this.stateWorker = new stateWorker()
118
const moduleModifiers = ModifiersWorker.getModuleModifiers(modifiers || [], this)
120
for (const moduleName of Object.keys(modulesMap)) {
123
modulesMap[moduleName],
130
* Create snapshot of the current store state
132
* @memberof StonexStore
133
* @returns {StateSnapshot<MP>}
135
public createStateSnapshot = (): StateSnapshot<MP> => StonexStore.createStateSnapshot(this.modules)
137
// tslint:disable:max-line-length
139
* Allows to attach stonex module to the store
142
* @param {string} moduleName - name of stonex module. This name will usings inside stonex store
143
* @param {(ModuleCreator<State, any> | ModuleConfiguration<State>)} data - It can be: stonex module class reference, pure stonex module or ModuleConfiguration
144
* @param {ModuleModifier[]} moduleModifiers - list of module modifiers (specific middleware)
145
* @returns {StonexModule<State>}
146
* @memberof StonexStore
150
* yourStore.connectModule('moduleName', ModuleClass)
152
* yourStore.modules.moduleName.methodFromModuleClass()
154
// tslint:enable:max-line-length
155
public connectModule<State> (
157
data: ModuleCreator<State, any> | ModuleConfiguration<State>,
158
moduleModifiers: ModuleModifier[] = []
159
): StonexModule<State> {
161
const createDefaultStoreBinder = () => createStoreBinder<MP, State>(moduleName, this)
163
const { module: Module, storeBinder = createDefaultStoreBinder() } =
165
isType(data, types.function)
166
|| (isType(data, types.object) && typeof (data as ModuleConfiguration<State>).module === 'undefined')
168
module: data as ModuleConfiguration<State>['module'],
169
storeBinder: createDefaultStoreBinder(),
170
} : data as ModuleConfiguration<State>
172
const moduleInstance = new (isPureModule(Module) ? convertToStandardModule(Module) : Module)(storeBinder)
174
const actionModifiers: ActionModifier[] = ModifiersWorker.getActionModifiers(moduleModifiers, moduleInstance)
176
if (!moduleInstance.__STONEXMODULE__) {
177
console.error(`${name} is not a Stonex Module` + '\r\n' +
178
`To solve this you should extend your class ${name} from StonexModule class`)
181
moduleInstance.__initialState = copy(moduleInstance.state)
182
this.stateWorker.initializeState(moduleInstance)
184
ModifiersWorker.attachActionModifiersToModule(actionModifiers, moduleInstance)
186
this.modules[moduleName] = moduleInstance
188
return moduleInstance
192
* Update Stonex module state
194
* @param {string} moduleName - name of module
195
* @param {*} changes - changes which need to apply to state of module
196
* @param {function} callback - function which will been called when state has been changed
198
* @memberof StonexStore
201
public setState = <State>(
203
changes: Partial<State> | ((state: State) => Partial<State>),
204
callback: (state: State) => any = noop
206
this.stateWorker.setState(this.getModuleByName(moduleName), changes, callback)
209
* Returns Stonex module state
211
* @param {string} moduleName
213
* @memberof StonexStore
216
public getState = (moduleName: string): any =>
217
this.stateWorker.getState(moduleName)
220
* Reset state of the Stonex module to initial value (first value of module state)
222
* @param {string} moduleName - name of module
223
* @param {function} callback - function which will been called when state has been cleared
225
* @memberof StonexStore
228
public resetState = (moduleName: string, callback: (state: any) => any = noop): void =>
229
this.stateWorker.resetState(this.getModuleByName(moduleName), callback)
232
* Find module in Stonex store by name
235
* @memberof StonexStore
237
* @returns {(StonexModule | never)}
239
private getModuleByName = (moduleName: string): StonexModule<any> | never => {
240
const module = this.modules[moduleName]
242
throw new Error(`Module with name ${moduleName} is not exist in your stonex store`)
248
export default StonexStore