UNPKG

2.55 kBTypeScriptView Raw
1import { Subject } from 'rxjs';
2import { takeUntil } from 'rxjs/operators';
3import { R, React } from './common';
4import { IStore } from './types';
5import { Connector, ConnectorLoadedEvent } from './components/Connector';
6
7interface IConnectWrapperState {
8 store?: IStore<any, any>;
9}
10export interface IConnectProps {
11 store?: IStore<any, any>;
12}
13
14/**
15 * HoC factory for connecting a component to a store.
16 */
17export function connect<P>(
18 component: React.SFC<any> | React.ComponentClass<any> | string,
19 options: {
20 autoRender?: boolean;
21 path?: string;
22 } = {},
23): React.ComponentClass<P> {
24 // Options.
25 const autoRender =
26 options.autoRender === undefined ? true : options.autoRender;
27
28 // HoC.
29 class ConnectWrapper extends React.PureComponent<
30 P & IConnectProps,
31 IConnectWrapperState
32 > {
33 private loaded: ConnectorLoadedEvent;
34 public state: IConnectWrapperState = {};
35 private unmounted$ = new Subject();
36
37 private get store(): IStore<any, any> | undefined {
38 if (this.props.store) {
39 // An explicit store was passed in as prop, use this instead of the one from the <Provider>.
40 return this.props.store;
41 }
42
43 // Read store that was passed via the <Provider>.
44 const store = this.loaded && this.loaded.store;
45 const { path } = options;
46 const result = path ? store.get(path) : store;
47 return R.is(Object, result) ? result : undefined;
48 }
49
50 private init = (e: ConnectorLoadedEvent) => {
51 this.loaded = e;
52 };
53
54 public componentDidMount() {
55 const store = this.store;
56 this.setState({ store });
57 if (store && autoRender) {
58 store.state$.pipe(takeUntil(this.unmounted$)).subscribe(() => {
59 try {
60 this.forceUpdate();
61 } catch (error) {
62 this.unmounted$.next();
63 }
64 });
65 }
66 }
67
68 public componentWillUnmount() {
69 this.unmounted$.next();
70 }
71
72 public render() {
73 return (
74 <Connector onLoaded={this.init}>{this.renderComponent()}</Connector>
75 );
76 }
77
78 private renderComponent() {
79 const { store } = this.state;
80 if (!store) {
81 // Don't render anything if the store has not been retrieved yet.
82 // Ensure the first render of the component has a store.
83 return '';
84 }
85
86 const props: any = {
87 ...(this.props as object),
88 store,
89 };
90 return React.createElement(component, props);
91 }
92 }
93
94 // Finish up.
95 return ConnectWrapper as React.ComponentClass<P>;
96}