UNPKG

undux

Version:

Dead simple state management for React

114 lines (100 loc) 3.06 kB
import { Observable as ObservableType } from 'rxjs' import { Observable, Observer } from 'rxjs-observable' export type ALL = '__ALL__' const ALL: ALL = '__ALL__' interface State<Messages extends object> { callChain: Set<keyof Messages | ALL> observables: Map<keyof Messages | ALL, Observable<any>[]> observers: Map<keyof Messages | ALL, Observer<any>[]> } const CYCLE_ERROR_MESSAGE = '[undux] Error: Cyclical dependency detected. ' + 'This may cause a stack overflow unless you fix it. \n' + 'The culprit is the following sequence of .set calls, ' + 'called from one or more of your Undux Effects: ' export class Emitter<Messages extends object> { private state: State<Messages> = { callChain: new Set(), observables: new Map(), observers: new Map(), } constructor(private isDevMode = false) {} /** * Emit an event (silently fails if no listeners are hooked up yet) */ emit<K extends keyof Messages>(key: K, value: Messages[K]): this { if (this.isDevMode) { if (this.state.callChain.has(key)) { console.error( CYCLE_ERROR_MESSAGE + Array.from(this.state.callChain).concat(key).join(' -> '), ) return this } else { this.state.callChain.add(key) } } if (this.hasChannel(key)) { this.emitOnChannel(key, value) } if (this.hasChannel(ALL)) { this.emitOnChannel(ALL, value) } if (this.isDevMode) this.state.callChain.clear() return this } /** * Subscribe to an event */ on<K extends keyof Messages>(key: K): ObservableType<Messages[K]> { return this.createChannel(key) as any } /** * Subscribe to all events */ all(): ObservableType<Messages[keyof Messages]> { return this.createChannel(ALL) as any } /// private private createChannel<K extends keyof Messages>(key: K | ALL) { if (!this.state.observers.has(key)) { this.state.observers.set(key, []) } if (!this.state.observables.has(key)) { this.state.observables.set(key, []) } const observable: Observable<Messages[K]> = new Observable((_) => { this.state.observers.get(key)!.push(_) return () => this.deleteChannel(key, observable) }) this.state.observables.get(key)!.push(observable) return observable } private deleteChannel<K extends keyof Messages>( key: K | ALL, observable: Observable<Messages[K]>, ) { if (!this.state.observables.has(key)) { return } const array = this.state.observables.get(key)! const index = array.indexOf(observable) if (index < 0) { return } array.splice(index, 1) if (!array.length) { this.state.observables.delete(key) this.state.observers.delete(key) } } private emitOnChannel<K extends keyof Messages>( key: K | ALL, value: Messages[K], ) { this.state.observers.get(key)!.forEach((_) => _.next(value)) } private hasChannel<K extends keyof Messages>(key: K | ALL): boolean { return this.state.observables.has(key) } }