Skip to content

Instantly share code, notes, and snippets.

@diestrin
Last active November 1, 2019 11:04
Show Gist options
  • Select an option

  • Save diestrin/f9ebd230fc80c62563d06e15d1240360 to your computer and use it in GitHub Desktop.

Select an option

Save diestrin/f9ebd230fc80c62563d06e15d1240360 to your computer and use it in GitHub Desktop.
Reduce Pattern in TypeScripe
// lib imports
import {ReplaySubject} from 'rxjs/ReplaySubject';
// src imports
import {Reducer, IReducerAction, IReduceCases} from './reducer';
export enum GateActions {
OPEN,
CLOSE,
TOGGLE
};
export interface IGateAction extends IReducerAction {
type: GateActions;
}
export interface IGateState {
status: boolean;
}
export const INITIAL_GATE_STATE: IGateState = {
status: false
};
export class GateService extends Reducer<IGateState> {
public dispatcher: ReplaySubject<IGateAction>;
protected cases: IReduceCases<IGateState> = {
[GateActions.OPEN]: GateService.open,
[GateActions.CLOSE]: GateService.close,
[GateActions.TOGGLE]: GateService.toggle
};
private static open(state: IGateState): IGateState {
return Object.assign({}, state, {status: true});
}
private static close(state: IGateState): IGateState {
return Object.assign({}, state, {status: false});
}
private static toggle(state: IGateState): IGateState {
return Object.assign({}, state, {status: !state.status});
}
constructor() {
super(INITIAL_GATE_STATE);
}
}
// lib imports
import {Component, ChangeDetectionStrategy} from '@angular/core';
import {Observable} from 'rxjs/Observable';
// src imports
import {
GateService,
IGateState,
GateActions
} from './gate.service';
@Component({
selector: 'home',
changeDetection: ChangeDetectionStrategy.OnPush
})
export class HomeComponent {
public status$: Observable<IGateState>;
constructor(private gateService: GateService) {
this.status$ = this.gateService.state;
}
public toggleGarageState(): void {
this.gateService.dispatcher.next({type: GateActions.TOGGLE});
}
}
import {Observable} from 'rxjs/Observable';
import {ReplaySubject} from 'rxjs/ReplaySubject';
import {BehaviorSubject} from 'rxjs/BehaviorSubject';
import 'rxjs/add/operator/scan';
import 'rxjs/add/operator/share';
export interface IReducerAction {
type: any;
}
export interface IReduceCases<State> {
[key: number]: (state: State, action: IReducerAction) => State;
}
export abstract class Reducer<State> {
public dispatcher: ReplaySubject<IReducerAction> =
new ReplaySubject<IReducerAction>();
public state: BehaviorSubject<State> = this
.wrapIntoBehaviorSubject(this.intialState, this.dispatcher
.scan(this.scan.bind(this), this.intialState).share());
// TO OVERRIDE
protected cases: IReduceCases<State>;
constructor(private intialState: State) {}
private scan(state: State = this.intialState,
action: IReducerAction
): State {
if (action.type in this.cases) {
return this.cases[action.type](state, action);
} else {
return state;
}
}
private wrapIntoBehaviorSubject(init: State,
obs: Observable<State>
): BehaviorSubject<State> {
const res: BehaviorSubject<State> = new BehaviorSubject<State>(init);
obs.subscribe((s: State) => {
return res.next(s);
});
return res;
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment