import { Observable, merge, Subject, of, combineLatest } from 'rxjs'
import { map, mapTo, skip, startWith, scan } from 'rxjs/operators'
import React from 'react'
import ReactDOM from 'react-dom'
const createEventObservable = <T extends {} = any>(): [Observable<T>, (e: T) => void] => {
let subject$ = new Subject<T>()
const observable = subject$.asObservable()
const handler = e => subject$.next(e)
return [observable, handler]
}
const toState = <T extends {} = any>(reducer$: Observable<(state: T) => T>): Observable<T> =>
reducer$.pipe(
startWith(undefined as unknown),
// @ts-ignore
scan((x, f) => f && f(x)),
skip(1),
)
const App = (): Observable<JSX.Element> => {
const [addVdom$, addReducer$] = Add()
const [subtractVdom$, subtractReducer$] = Subtract()
const reducer$ = of(() => 0)
const state$ = toState( merge(addReducer$, subtractReducer$, reducer$) )
return combineLatest(state$, addVdom$, subtractVdom$).pipe(
map(([count, addVdom, subtractVdom]) => (
<>
<p>Current count: { count }</p>
<p>{ addVdom } { subtractVdom }</p>
</>
))
)
}
const Add = (): [Observable<JSX.Element>, Observable<(x: number) => number>] => {
const [observable, handler] = createEventObservable<React.MouseEvent<HTMLButtonElement, MouseEvent>>()
return [
of(<button onClick={handler}>add count</button>),
observable.pipe(mapTo((x: number) => x + 1)),
]
}
const Subtract = (): [Observable<JSX.Element>, Observable<(x: number) => number>] => {
const [observable, handler] = createEventObservable<React.MouseEvent<HTMLButtonElement, MouseEvent>>()
return [
of(<button onClick={handler}>subtract count</button>),
observable.pipe(mapTo((x: number) => x - 1)),
]
}
const vdom$ = App()
vdom$.subscribe(
vdom => {
ReactDOM.render(vdom, document.getElementById('app'))
}
)