Last active
January 17, 2018 07:45
-
-
Save typoerr/ca55ec31f887b5c45e30a5a0b3890c05 to your computer and use it in GitHub Desktop.
State management by RxJS
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { Observable, Subject, BehaviorSubject } from 'rxjs' | |
interface State { | |
counter: { count: number }, | |
user: { name: string, age: number } | |
} | |
const models = { | |
count(intent: { inc$: Observable<number>, dec$: Observable<number> }) { | |
type S = State['counter'] | |
return Observable.of((_s: S) => ({ count: 0 })).merge( | |
intent.inc$.map(n => (s: S) => ({ count: s.count + n })), | |
intent.dec$.map(n => (s: S) => ({ count: s.count - n })), | |
) | |
}, | |
user(intent: { update$: Observable<{ name?: string, age?: number }> }) { | |
type S = State['user'] | |
return Observable.of((_: S) => ({ name: 'unknown', age: 0 })).merge( | |
intent.update$.pluck<Partial<S>, string>('name') | |
.filter(Boolean) | |
.map((name => (s: S) => ({ name, age: s.age }))), | |
intent.update$.pluck<Partial<S>, number>('age') | |
.filter(Boolean) | |
.map((age => (s: S) => ({ name: s.name, age }))), | |
) | |
}, | |
} | |
const actions = { | |
inc$: new Subject<number>(), | |
dec$: new Subject<number>(), | |
update$: new Subject<{ name?: string, age?: number }>(), | |
} | |
const intents = { | |
inc$: actions.inc$.map(n => n * 10), | |
dec$: actions.dec$.map(n => n * 10), | |
update$: actions.update$, | |
} | |
const state$ = new BehaviorSubject<State>({} as any) | |
/* subscribe */ | |
const subscription = attachMutations(state$, [ | |
models.count(intents).let(makeStateful).map(s => ({ counter: s })), | |
models.user(intents).let(makeStateful).map(s => ({ user: s })), | |
]) | |
// unsubscribe all mutation | |
state$.filter(s => s === null).subscribe(() => { | |
subscription.unsubscribe() | |
}) | |
state$.map(state => state.user) | |
.distinctUntilChanged() | |
.subscribe(console.log) | |
state$.pluck<any, number>('counter', 'count') | |
.distinctUntilChanged() | |
.subscribe(console.log) | |
/* kick action */ | |
actions.inc$.next(1) | |
actions.inc$.next(1) | |
actions.dec$.next(1) | |
actions.inc$.next(10) | |
actions.update$.next({ name: 'taro' }) | |
actions.update$.next({ age: 10 }) | |
actions.update$.next({ name: 'taro yamada', age: 20 }) | |
// | |
// ─── LIB ──────────────────────────────────────────────────────────────────────── | |
// | |
function makeStateful<T>(reducer$: Observable<(state: T) => T>) { | |
return reducer$.scan((acc, fn) => fn(acc), {} as T) | |
.distinctUntilChanged() | |
.shareReplay(1) | |
} | |
// tslint:disable:no-shadowed-variable | |
function attachMutations<T>(state$: Subject<T>, mutations: Observable<Partial<T>>[]) { | |
return Observable.merge(...mutations) | |
.withLatestFrom(state$, assign) | |
.subscribe({ next, error, complete }) | |
function assign(next: Partial<T>, cur: T) { | |
return Object.assign({}, cur, next) | |
} | |
function next(value: T) { | |
return state$.next(value) | |
} | |
function error(err: any) { | |
console.error(err) | |
} | |
function complete() { | |
/* noop */ | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
https://github.com/cotttpan/frxx