Skip to content

Instantly share code, notes, and snippets.

@evgenyfedorenko
Last active November 4, 2020 16:29
Show Gist options
  • Save evgenyfedorenko/589f67d3b32afbc035d497dd2a02745e to your computer and use it in GitHub Desktop.
Save evgenyfedorenko/589f67d3b32afbc035d497dd2a02745e to your computer and use it in GitHub Desktop.
import { Injectable } from '@angular/core';
import {
BehaviorSubject,
Observable,
queueScheduler,
} from 'rxjs';
import { observeOn, scan, withLatestFrom, map, distinctUntilChanged } from 'rxjs/operators';
import { reducers } from './reducers';
export interface Action {
type: string;
}
interface ActionReducer<T, V extends Action = Action> {
(state: T | undefined, action: V): T;
}
@Injectable()
export class StoreService {
actions$: BehaviorSubject<Action>;
state$: BehaviorSubject<any>;
constructor() {
this.actions$ = new BehaviorSubject({ type: 'INIT' });
const actionsOnQueue$: Observable<Action> = this.actions$.pipe(observeOn(queueScheduler));
const reducer$: Observable<
ActionReducer<any, any>
> = new BehaviorSubject(reducerFactory(reducers));
const withLatestReducer$: Observable<
[Action, ActionReducer<any, Action>]
> = actionsOnQueue$.pipe(withLatestFrom(reducer$));
this.state$ = new BehaviorSubject({});
const stateAndAction$: Observable<
{ state: any; action?: Action; }
> = withLatestReducer$.pipe(
scan(reduceState, { state: {} }
)
);
stateAndAction$.subscribe(({ state }) => {
this.state$.next(state);
});
}
getState(): BehaviorSubject<any> {
return this.state$;
}
dispatch(action: Action) {
this.actions$.next(action);
}
}
function reduceState(stateActionPair = { state: undefined }, [action, reducer]) {
const { state } = stateActionPair;
return { state: reducer(state, action), action };
}
function reducerFactory(reducers) {
return function combination(state, action) {
const nextState: any = {};
const reducerKeys = Object.keys(reducers);
for (let i = 0; i < reducerKeys.length; i++) {
const key = reducerKeys[i];
const reducer: any = reducers[key];
nextState[key] = reducer(state[key], action);
}
return nextState;
}
}
export function select<T, Props, K>(selector: (state: T, props?: Props) => any) {
return function selectOperator(source$: Observable<T>): Observable<K> {
let mapped$: Observable<any>;
mapped$ = source$.pipe(
map(source => selector(source))
);
return mapped$.pipe(distinctUntilChanged());
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment