-
Notifications
You must be signed in to change notification settings - Fork 12
Expand file tree
/
Copy pathContainersProvider.tsx
More file actions
112 lines (89 loc) · 3.34 KB
/
Copy pathContainersProvider.tsx
File metadata and controls
112 lines (89 loc) · 3.34 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
import React from 'react';
import { bind } from 'decko';
import { injectable } from 'inversify';
import { Omit, SubSet } from '_helpers';
import * as usersSearchFeature from 'features/usersSearch';
import { inject, TYPES, IocTypes } from './configure/ioc';
import { IFeatureEntry, IReduxEntry } from './types';
interface IContainerTypes {
UserDetails: usersSearchFeature.Entry['containers']['UserDetails'];
}
type Container = keyof IContainerTypes;
type IEntryWithContainer<K extends string, T extends React.ComponentType<any>> = SubSet<IFeatureEntry, {
containers: { [D in K]: T };
reduxEntry?: IReduxEntry;
}>;
type Loader<T extends Container> = () => Promise<IEntryWithContainer<T, IContainerTypes[T]>>;
type LoadersMap = {
[P in Container]: Loader<P>;
};
type GenericLoadersMap = {
[P in Container]: Loader<Container>;
};
const containerLoadersDictionary: LoadersMap = {
UserDetails: usersSearchFeature.loadEntry,
};
interface IState {
containers: {
[key: string]: React.ComponentType<any>;
};
}
// tslint:disable:max-line-length
function containersProvider<L extends Container>(containers: L[], preloader?: React.ReactChild):
<Props extends { [K in L]: IContainerTypes[K] }>(WrappedComponent: React.ComponentType<Props>) => React.ComponentClass<Omit<Props, L>> {
return <Props extends { [K in L]: IContainerTypes[K] }>(
WrappedComponent: React.ComponentType<Props>,
): React.ComponentClass<Omit<Props, L>> => {
@injectable()
class ContainersProvider extends React.PureComponent<Props, IState> {
public state: IState = { containers: {} };
@inject(TYPES.connectEntryToStore)
private connectFeatureToStore!: IocTypes[typeof TYPES.connectEntryToStore];
public componentDidMount() {
this.load();
}
public componentWillUnmount() {
this.saveContainerToState = null;
}
// TODO: УДОЛИ
public render() {
if (!this.isAllContainersLoaded()) {
return preloader !== void 0 ? preloader : null;
} else {
return <WrappedComponent {...this.state.containers} {...this.props} />;
}
}
@bind
private async load(): Promise<void> {
await Promise.all(containers.map(key => this.loadFeatureContainer(key)));
}
@bind
private async loadFeatureContainer(containerKey: Container): Promise<void> {
const bundle = await (containerLoadersDictionary as GenericLoadersMap)[containerKey]();
const container = bundle.containers[containerKey];
bundle.reduxEntry && this.connectFeatureToStore(bundle.reduxEntry);
if (!container) {
throw new Error(`ContainersProvider did not find the container "${containerKey}"`);
}
this.saveContainerToState && this.saveContainerToState(container, containerKey);
}
private saveContainerToState: null | ((container: React.ComponentType<any>, key: string) => void) =
(cont, key) => {
this.setState(state => ({
...state,
containers: {
...state.containers,
[key]: cont,
},
}));
}
@bind
private isAllContainersLoaded(): boolean {
return containers.every(key => Boolean(this.state.containers[key]));
}
}
return ContainersProvider;
};
}
export { IContainerTypes };
export default containersProvider;