stonex

Форк
0
/
StonexStore.ts 
248 строк · 7.4 Кб
1
import {
2
  ModuleConfiguration, ModuleCreator, ModuleCreatorsMap,
3
  StateSnapshot,
4
  StonexModules
5
} from '.'
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'
12

13
/**
14
 * @typedef {Object} MP - Map of Stonex Modules class references
15
 */
16

17
export declare interface Store<MP> {
18
  modules: StonexModules<MP>
19
  getState: <State>(moduleName: string) => State
20
  setState: <State>(
21
    moduleName: string,
22
    changes: ((() => Partial<State>) | Partial<State>), callback?: (state: State) => any
23
  ) => any
24
  resetState: (moduleName: string, callback?: (state: any) => any) => void
25
  connectModule: <State> (
26
    moduleName: string,
27
    data: ModuleCreator<State, any>
28
  ) => StonexModule<State>
29
  createStateSnapshot: () => StateSnapshot<MP>,
30
  storeId: number
31
}
32

33
export declare interface StoreConfiguration<MP> {
34
  stateWorker?: new (...args: any[]) => StateWorker,
35
  modifiers?: Array<Modifier<MP>>
36
}
37

38
/**
39
 * Creates a new stonex store.
40
 * Combining all your stonex modules together and allows to use them in your application.
41
 *
42
 * @class StonexStore
43
 * @implements {Store<MP>}
44
 * @template MP
45
 *
46
 * @example
47
 * import SomeModule from './SomeModule'
48
 *
49
 * const store = new StonexStore({
50
 *  someModule: SomeModule
51
 * })
52
 *
53
 * store.modules.someModule.doSomething()
54
 *
55
 * store.createStateSnapshot()
56
 */
57
class StonexStore<MP> implements Store<MP> {
58

59
  /**
60
   * Creates snapshot of state
61
   *
62
   * @param {MP} modules - Map with keys where key it is name of module and value it is class reference or object
63
   *
64
   * @static
65
   * @memberof StonexStore
66
   *
67
   * @returns {StateSnapshot<MP>}
68
   */
69
  public static createStateSnapshot = <MP>(modules: MP): StateSnapshot<MP> =>
70
    Object.keys(modules).reduce((state, name) => {
71
      state[name] = copy(modules[name].state)
72
      return state
73
    }, {}) as StateSnapshot<MP>
74

75
  /**
76
   * Unique identificator of store.
77
   * Usings inside Stonex Store. Don't change it!
78
   *
79
   * @type {number}
80
   * @memberof StonexStore
81
   */
82
  public storeId: number = Math.round(Math.random() * Number.MAX_SAFE_INTEGER - Date.now())
83

84
  /**
85
   * Map of the Stonex modules
86
   *
87
   * @type {StonexModules<MP>}
88
   * @memberof StonexStore
89
   */
90
  public modules: StonexModules<MP> = {} as StonexModules<MP>
91

92
  /**
93
   * Set of methods which needed to work with module's state (updating, initializing, etc)
94
   *
95
   * Its can be overriden via StonexStore constructor:
96
   * @example
97
   * new StonexStore(modules, { stateWorker: OwnStateWorker })
98
   *
99
   * @private
100
   * @type {StateWorker}
101
   * @memberof StonexStore
102
   */
103
  private stateWorker: StateWorker
104

105
  /**
106
   * Creates an instance of StonexStore.
107
   *
108
   * @param {Partial<ModuleCreatorsMap<MP>>} modulesMap
109
   * @param {StoreConfiguration<MP>} storeConfiguration - have keys 'stateWorker', 'modifiers'
110
   * @memberof StonexStore
111
   */
112
  constructor (
113
    modulesMap: Partial<ModuleCreatorsMap<MP>>,
114
    { stateWorker = StateWorker, modifiers }: StoreConfiguration<MP> = {}
115
  ) {
116
    this.stateWorker = new stateWorker()
117

118
    const moduleModifiers = ModifiersWorker.getModuleModifiers(modifiers || [], this)
119

120
    for (const moduleName of Object.keys(modulesMap)) {
121
      this.connectModule(
122
        moduleName,
123
        modulesMap[moduleName],
124
        moduleModifiers
125
      )
126
    }
127
  }
128

129
  /**
130
   * Create snapshot of the current store state
131
   *
132
   * @memberof StonexStore
133
   * @returns {StateSnapshot<MP>}
134
   */
135
  public createStateSnapshot = (): StateSnapshot<MP> => StonexStore.createStateSnapshot(this.modules)
136

137
  // tslint:disable:max-line-length
138
  /**
139
   * Allows to attach stonex module to the store
140
   *
141
   * @template State
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
147
   *
148
   *
149
   * @example
150
   * yourStore.connectModule('moduleName', ModuleClass)
151
   *
152
   * yourStore.modules.moduleName.methodFromModuleClass()
153
   */
154
  // tslint:enable:max-line-length
155
  public connectModule<State> (
156
    moduleName: string,
157
    data: ModuleCreator<State, any> | ModuleConfiguration<State>,
158
    moduleModifiers: ModuleModifier[] = []
159
  ): StonexModule<State> {
160

161
    const createDefaultStoreBinder = () => createStoreBinder<MP, State>(moduleName, this)
162

163
    const { module: Module, storeBinder = createDefaultStoreBinder() } =
164
      (
165
        isType(data, types.function)
166
        || (isType(data, types.object) && typeof (data as ModuleConfiguration<State>).module === 'undefined')
167
      ) ? {
168
        module: data as ModuleConfiguration<State>['module'],
169
        storeBinder: createDefaultStoreBinder(),
170
      } : data as ModuleConfiguration<State>
171

172
    const moduleInstance = new (isPureModule(Module) ? convertToStandardModule(Module) : Module)(storeBinder)
173

174
    const actionModifiers: ActionModifier[] = ModifiersWorker.getActionModifiers(moduleModifiers, moduleInstance)
175

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`)
179
    }
180

181
    moduleInstance.__initialState = copy(moduleInstance.state)
182
    this.stateWorker.initializeState(moduleInstance)
183

184
    ModifiersWorker.attachActionModifiersToModule(actionModifiers, moduleInstance)
185

186
    this.modules[moduleName] = moduleInstance
187

188
    return moduleInstance
189
  }
190

191
  /**
192
   * Update Stonex module state
193
   *
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
197
   *
198
   * @memberof StonexStore
199
   * @returns {void}
200
   */
201
  public setState = <State>(
202
    moduleName: string,
203
    changes: Partial<State> | ((state: State) => Partial<State>),
204
    callback: (state: State) => any = noop
205
  ): void =>
206
    this.stateWorker.setState(this.getModuleByName(moduleName), changes, callback)
207

208
  /**
209
   * Returns Stonex module state
210
   *
211
   * @param {string} moduleName
212
   *
213
   * @memberof StonexStore
214
   * @returns {*}
215
   */
216
  public getState = (moduleName: string): any =>
217
    this.stateWorker.getState(moduleName)
218

219
  /**
220
   * Reset state of the Stonex module to initial value (first value of module state)
221
   *
222
   * @param {string} moduleName - name of module
223
   * @param {function} callback - function which will been called when state has been cleared
224
   *
225
   * @memberof StonexStore
226
   * @returns {void}
227
   */
228
  public resetState = (moduleName: string, callback: (state: any) => any = noop): void =>
229
    this.stateWorker.resetState(this.getModuleByName(moduleName), callback)
230

231
  /**
232
   * Find module in Stonex store by name
233
   *
234
   * @private
235
   * @memberof StonexStore
236
   *
237
   * @returns {(StonexModule | never)}
238
   */
239
  private getModuleByName = (moduleName: string): StonexModule<any> | never => {
240
    const module = this.modules[moduleName]
241
    if (!module) {
242
      throw new Error(`Module with name ${moduleName} is not exist in your stonex store`)
243
    }
244
    return module
245
  }
246
}
247

248
export default StonexStore
249

Использование cookies

Мы используем файлы cookie в соответствии с Политикой конфиденциальности и Политикой использования cookies.

Нажимая кнопку «Принимаю», Вы даете АО «СберТех» согласие на обработку Ваших персональных данных в целях совершенствования нашего веб-сайта и Сервиса GitVerse, а также повышения удобства их использования.

Запретить использование cookies Вы можете самостоятельно в настройках Вашего браузера.