Skip to content

Instantly share code, notes, and snippets.

@typoerr
Last active July 22, 2017 13:50
Show Gist options
  • Select an option

  • Save typoerr/fe80de3f64c6bf32f40fdddc4ab0c8f3 to your computer and use it in GitHub Desktop.

Select an option

Save typoerr/fe80de3f64c6bf32f40fdddc4ab0c8f3 to your computer and use it in GitHub Desktop.
/*
- storeとつながる
- selectorをstreamで書ける
*/
import { h, Component, ComponentConstructor, FunctionalComponent } from 'preact';
import { Stream, Subscription, from, mergeArray } from 'most';
import { async } from 'most-subject';
import { Store } from 'redux';
import { noop } from '@utils';
const assign = (o1: any, o2: any) => Object.assign({}, o1, o2);
export type AnyComponent<P> = ComponentConstructor<P, any> | FunctionalComponent<P>;
export interface RequireCtx<S = any> {
store: Store<S>;
}
export interface InitSelector<CP, S, OP = {}, C= {}> {
(state: S, initialProps: OP, context: C): CP;
}
export interface Selector<CP, S, OP = {}, C = {}> {
(state$: Stream<S>, initialProps: OP, context: C): Stream<Partial<CP>>;
}
export function connect<CP, S, OP>(init: InitSelector<CP, S, OP>, ...selector: Selector<CP, S, OP>[]) {
return (component: AnyComponent<CP>): ComponentConstructor<OP, any> => {
return class WrapperComponent extends Component<OP, any> {
static displayName = `Connect(${component.name})`;
context: RequireCtx<S>;
nextProps = {};
protected store = this.context.store;
protected state$ = from<S>(this.context.store as any).multicast();
protected in$ = async<S>();
protected subscriptions: Subscription<any>[] = [];
constructor(props: any, context: any) {
super(props, context);
const state = this.store.getState();
this.nextProps = init(state, props, context);
const sub = this.state$.subscribe({
next: (s) => this.in$.next(s),
error: (e) => { throw e; },
complete: noop
});
this.subscriptions.push(sub);
}
componentWillMount() {
const next = () => this.forceUpdate();
const error = (err: Error) => { throw err; };
const subscriber = { next, error, complete: noop };
const arr$ = selector.map(s => s(this.in$, this.props, this.context));
const selectors$ = mergeArray(arr$);
const sub = selectors$
.scan(assign, this.nextProps)
.tap(x => this.nextProps = x)
.debounce(0) // 連続した同期的なstreamによるviewの更新を間引く
.subscribe(subscriber);
this.subscriptions.push(sub);
}
componentWillUnMount() {
this.subscriptions.forEach(sub => sub.unsubscribe());
}
render(props: any) {
return h(component, { ...props, ...this.nextProps });
}
};
};
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment