propsをstreamで受け取れるようにするpreact hoc
initial propsを受け取りに難がある。完成度が低い。
| import { h, Component, ComponentConstructor, FunctionalComponent } from 'preact'; | |
| import { Stream, Subscription, mergeArray } from 'most'; | |
| import { noop, hasOwn } from '@utils'; | |
| export type AnyComponent<P> = ComponentConstructor<P, any> | FunctionalComponent<P>; | |
| export type StreamableProps<P> = { | |
| [K in keyof P]: Stream<P[K]> | P[K] | |
| }; | |
| export function isStream(x: any) { | |
| return x instanceof Stream; | |
| } | |
| export default function observer<P>(component: AnyComponent<P>, initProps?: Partial<P>) { | |
| return class ObserverComponent extends Component<StreamableProps<P>, {}> { | |
| static displayName = `Observer(${component.name})`; | |
| subscriptions: Subscription<any>[] = []; | |
| currentProps: any = initProps || {}; | |
| constructor(props: StreamableProps<P>, context: any) { | |
| super(props, context); | |
| } | |
| componentWillMount() { | |
| const p: any = this.props; | |
| const streams: Stream<any>[] = []; | |
| for (const k in p) { | |
| if (!hasOwn(p, k)) continue; | |
| if (!isStream(p[k])) { | |
| this.currentProps[k] = p[k]; | |
| continue; | |
| } | |
| const v = (p[k] as Stream<any>); | |
| const s$ = v.tap(x => this.currentProps[k] = x); | |
| streams.push(s$); | |
| } | |
| // reduce synchronous streams by debounce | |
| const sub = mergeArray(streams).debounce(0).subscribe({ | |
| next: () => this.forceUpdate(), | |
| error: (e: Error) => { throw e; }, | |
| complete: noop | |
| }); | |
| this.subscriptions.push(sub); | |
| } | |
| componentWillUnMount() { | |
| this.subscriptions.forEach(x => x.unsubscribe()); | |
| } | |
| componentWillReceiveProps(p: any) { | |
| for (const k in p) { | |
| if (!hasOwn(p, k) || isStream(p[k])) continue; | |
| this.currentProps[k] = p[k]; | |
| } | |
| } | |
| render() { | |
| return h(component, this.currentProps, this.props.children!); | |
| } | |
| }; | |
| } |