forked from launchdarkly/react-client-sdk
-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathprovider.tsx
93 lines (80 loc) · 3.34 KB
/
provider.tsx
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
import * as React from 'react';
import { LDClient, LDFlagSet, LDFlagChangeset } from 'launchdarkly-js-client-sdk';
import { EnhancedComponent, ProviderConfig, defaultReactOptions } from './types';
import { Provider, LDContext as HocState } from './context';
import initLDClient from './initLDClient';
import {camelCaseKeys, getFlattenedFlagsFromChangeset} from './utils';
/**
* The `LDProvider` is a component which accepts a config object which is used to
* initialize `launchdarkly-js-client-sdk`.
*
* This Provider does three things:
* - It initializes the ldClient instance by calling `launchdarkly-js-client-sdk` initialize on `componentDidMount`
* - It saves all flags and the ldClient instance in the context API
* - It subscribes to flag changes and propagate them through the context API
*
* Because the `launchdarkly-js-client-sdk` in only initialized on `componentDidMount`, your flags and the
* ldClient are only available after your app has mounted. This can result in a flicker due to flag changes at
* startup time.
*
* This component can be used as a standalone provider. However, be mindful to only include the component once
* within your application. This provider is used inside the `withLDProviderHOC` and can be used instead to initialize
* the `launchdarkly-js-client-sdk`. For async initialization, check out the `asyncWithLDProvider` function
*/
class LDProvider extends React.Component<ProviderConfig, HocState> implements EnhancedComponent {
readonly state: Readonly<HocState>;
constructor(props: ProviderConfig) {
super(props);
const { options } = props;
this.state = {
flags: {},
ldClient: undefined,
};
if (options) {
const { bootstrap } = options;
if (bootstrap && bootstrap !== 'localStorage') {
const { useCamelCaseFlagKeys } = this.getReactOptions();
const flags = useCamelCaseFlagKeys ? camelCaseKeys(bootstrap) : bootstrap;
this.state = {
flags,
ldClient: undefined,
};
}
}
}
getReactOptions = () => ({ ...defaultReactOptions, ...this.props.reactOptions });
subscribeToChanges = (ldClient: LDClient) => {
const { flags } = this.props;
ldClient.on('change', (changes: LDFlagChangeset) => {
const flattened: LDFlagSet | null = getFlattenedFlagsFromChangeset(changes, flags, this.getReactOptions());
if (flattened) {
this.setState(({flags}) => ({flags: {...flags, ...flattened}}));
}
});
};
initLDClient = async () => {
const { clientSideID, flags, options, user } = this.props;
const reactOptions = this.getReactOptions();
const { flags: fetchedFlags, ldClient } = await initLDClient(clientSideID, user, reactOptions, options, flags);
this.setState({ flags: fetchedFlags, ldClient });
this.subscribeToChanges(ldClient);
};
async componentDidMount() {
const { user, deferInitialization } = this.props;
if (deferInitialization && !user) {
return;
}
await this.initLDClient();
}
async componentDidUpdate(prevProps: ProviderConfig) {
const { user, deferInitialization } = this.props;
const userJustLoaded = !prevProps.user && user;
if (deferInitialization && userJustLoaded) {
await this.initLDClient();
}
}
render() {
return <Provider value={this.state}>{this.props.children}</Provider>;
}
}
export default LDProvider;