@featbit/react-client-sdk 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/LICENSE +201 -0
  2. package/README.md +435 -0
  3. package/dist/asyncWithFbProvider.d.ts +29 -0
  4. package/dist/asyncWithFbProvider.js +129 -0
  5. package/dist/asyncWithFbProvider.js.map +1 -0
  6. package/dist/context.d.ts +15 -0
  7. package/dist/context.js +6 -0
  8. package/dist/context.js.map +1 -0
  9. package/dist/getFlagsProxy.d.ts +6 -0
  10. package/dist/getFlagsProxy.js +56 -0
  11. package/dist/getFlagsProxy.js.map +1 -0
  12. package/dist/index.d.ts +10 -0
  13. package/dist/index.js +11 -0
  14. package/dist/index.js.map +1 -0
  15. package/dist/initClient.d.ts +12 -0
  16. package/dist/initClient.js +84 -0
  17. package/dist/initClient.js.map +1 -0
  18. package/dist/provider.d.ts +39 -0
  19. package/dist/provider.js +201 -0
  20. package/dist/provider.js.map +1 -0
  21. package/dist/types.d.ts +90 -0
  22. package/dist/types.js +6 -0
  23. package/dist/types.js.map +1 -0
  24. package/dist/useFbClient.d.ts +11 -0
  25. package/dist/useFbClient.js +18 -0
  26. package/dist/useFbClient.js.map +1 -0
  27. package/dist/useFlags.d.ts +11 -0
  28. package/dist/useFlags.js +16 -0
  29. package/dist/useFlags.js.map +1 -0
  30. package/dist/utils.d.ts +27 -0
  31. package/dist/utils.js +78 -0
  32. package/dist/utils.js.map +1 -0
  33. package/dist/withFbConsumer.d.ts +38 -0
  34. package/dist/withFbConsumer.js +36 -0
  35. package/dist/withFbConsumer.js.map +1 -0
  36. package/dist/withFbProvider.d.ts +25 -0
  37. package/dist/withFbProvider.js +50 -0
  38. package/dist/withFbProvider.js.map +1 -0
  39. package/package.json +57 -0
  40. package/src/asyncWithFbProvider.tsx +72 -0
  41. package/src/context.ts +24 -0
  42. package/src/getFlagsProxy.ts +82 -0
  43. package/src/index.ts +21 -0
  44. package/src/initClient.ts +26 -0
  45. package/src/provider.tsx +136 -0
  46. package/src/types.ts +103 -0
  47. package/src/useFbClient.ts +21 -0
  48. package/src/useFlags.ts +19 -0
  49. package/src/utils.ts +39 -0
  50. package/src/withFbConsumer.tsx +58 -0
  51. package/src/withFbProvider.tsx +49 -0
@@ -0,0 +1,82 @@
1
+ import { defaultReactOptions, FbReactOptions, FlagKeyMap, IFlagSet } from "./types";
2
+ import { IFbClient } from "@featbit/js-client-sdk";
3
+ import camelCase from "lodash.camelcase";
4
+
5
+ export default function getFlagsProxy(
6
+ fbClient: IFbClient,
7
+ bootstrapFlags: IFlagSet,
8
+ fetchedFlags: IFlagSet,
9
+ reactOptions: FbReactOptions = defaultReactOptions
10
+ ): { flags: IFlagSet; flagKeyMap: FlagKeyMap } {
11
+ const { useCamelCaseFlagKeys = false, sendEventsOnFlagRead = true } = reactOptions;
12
+ const [flags, flagKeyMap = {}] = useCamelCaseFlagKeys ? getCamelizedKeysAndFlagMap(fetchedFlags) : [fetchedFlags];
13
+
14
+ return {
15
+ flags: toFlagsProxy(fbClient, bootstrapFlags, flags, flagKeyMap, fetchedFlags, useCamelCaseFlagKeys, sendEventsOnFlagRead),
16
+ flagKeyMap,
17
+ };
18
+ }
19
+
20
+ function getCamelizedKeysAndFlagMap(rawFlags: IFlagSet) {
21
+ const flags: IFlagSet = {};
22
+ const flagKeyMap: FlagKeyMap = {};
23
+ for (const rawFlagKey in rawFlags) {
24
+ // Exclude system keys
25
+ if (rawFlagKey.indexOf('$') === 0) {
26
+ continue;
27
+ }
28
+ const camelKey = camelCase(rawFlagKey);
29
+ flags[camelKey] = rawFlags[rawFlagKey];
30
+ flagKeyMap[camelKey] = rawFlagKey;
31
+ }
32
+
33
+ return [flags, flagKeyMap];
34
+ }
35
+
36
+ function hasFlag(flags: IFlagSet, flagKey: string) {
37
+ return Object.prototype.hasOwnProperty.call(flags, flagKey);
38
+ }
39
+
40
+ function toFlagsProxy(
41
+ fbClient: IFbClient,
42
+ bootstrapFlags: IFlagSet,
43
+ flags: IFlagSet,
44
+ flagKeyMap: FlagKeyMap,
45
+ flagsWithRawFlagKeys: IFlagSet,
46
+ useCamelCaseFlagKeys: boolean,
47
+ sendEventsOnFlagRead: boolean
48
+ ): IFlagSet {
49
+ return new Proxy(flags, {
50
+ get: (target, prop, receiver) => {
51
+ const currentValue = Reflect.get(target, prop, receiver) || flagsWithRawFlagKeys[prop as string]
52
+
53
+ // check if flag key exists as camelCase or original case
54
+ const validFlagKey =
55
+ hasFlag(flagKeyMap, prop as string) || hasFlag(target, prop as string) || hasFlag(flagsWithRawFlagKeys, prop as string);
56
+
57
+ if (!validFlagKey && hasFlag(bootstrapFlags, prop as string)) {
58
+ return bootstrapFlags[prop as string];
59
+ }
60
+
61
+ // only process flag keys and ignore symbols and native Object functions
62
+ if (typeof prop === 'symbol' || !validFlagKey) {
63
+ return currentValue;
64
+ }
65
+
66
+ if (useCamelCaseFlagKeys && prop !== camelCase(prop as string)) {
67
+ console.warn(`You're attempting to access a flag with its original keyId: ${prop as string}, even though useCamelCaseFlagKeys is set to true.`);
68
+ }
69
+
70
+ if (currentValue === undefined) {
71
+ return undefined;
72
+ }
73
+
74
+ if (!sendEventsOnFlagRead) {
75
+ return currentValue;
76
+ }
77
+
78
+ const pristineFlagKey = useCamelCaseFlagKeys ? (flagKeyMap[prop] || prop) : prop;
79
+ return fbClient.variation(pristineFlagKey, currentValue);
80
+ },
81
+ });
82
+ }
package/src/index.ts ADDED
@@ -0,0 +1,21 @@
1
+ import asyncWithFbProvider from './asyncWithFbProvider';
2
+ import context from './context';
3
+ import FbProvider from './provider';
4
+ import useFlags from './useFlags';
5
+ import useFbClient from './useFbClient';
6
+ import { camelCaseKeys } from './utils';
7
+ import withFbConsumer from './withFbConsumer';
8
+ import withFbProvider from './withFbProvider';
9
+
10
+ export * from './types';
11
+
12
+ export {
13
+ FbProvider,
14
+ context,
15
+ asyncWithFbProvider,
16
+ camelCaseKeys,
17
+ useFlags,
18
+ useFbClient,
19
+ withFbProvider,
20
+ withFbConsumer
21
+ };
@@ -0,0 +1,26 @@
1
+ import { AllFlagsFbClient, defaultReactOptions, FbReactOptions } from './types';
2
+ import { FbClientBuilder, IOptions } from '@featbit/js-client-sdk';
3
+ import { fetchFlags } from "./utils";
4
+
5
+ /**
6
+ * Internal function to initialize the `@featbit/js-client-sdk`.
7
+ *
8
+ * @param reactOptions Initialization options for the FeatBit React SDK
9
+ * @param options @featbit/js-client-sdk initialization options
10
+ *
11
+ * @see `ProviderConfig` for more details about the parameters
12
+ * @return An initialized client and flags
13
+ */
14
+ export const initClient = async (
15
+ reactOptions: FbReactOptions = defaultReactOptions,
16
+ options: IOptions = {}
17
+ ): Promise<AllFlagsFbClient> => {
18
+ const fbClient = new FbClientBuilder({...options}).build();
19
+
20
+ return new Promise<AllFlagsFbClient>((resolve) => {
21
+ fbClient.on('ready', async () => {
22
+ const flags = await fetchFlags(fbClient);
23
+ resolve({flags, fbClient});
24
+ });
25
+ });
26
+ };
@@ -0,0 +1,136 @@
1
+ import React from "react";
2
+ import { EnhancedComponent, ProviderConfig, defaultReactOptions, IFlagSet } from './types';
3
+ import { Provider, FbContext } from './context';
4
+ import { camelCaseKeys, fetchFlags } from "./utils";
5
+ import { initClient } from './initClient';
6
+ import { IFbClient } from '@featbit/js-client-sdk';
7
+ import getFlagsProxy from "./getFlagsProxy";
8
+
9
+ interface FbHocState extends FbContext {
10
+ unproxiedFlags: IFlagSet;
11
+ }
12
+
13
+ /**
14
+ * The `FbProvider` is a component which accepts a config object which is used to
15
+ * initialize `@featbit/js-client-sdk`.
16
+ *
17
+ * This Provider does three things:
18
+ * - It initializes the FeatBit instance by calling `@featbit/js-client-sdk` init on `componentDidMount`
19
+ * - It saves all flags and the FeatBit instance in the context API
20
+ * - It subscribes to flag changes and propagate them through the context API
21
+ *
22
+ * Because the `@featbit/js-client-sdk` is only initialized on `componentDidMount`, your flags and the
23
+ * FeatBit are only available after your app has mounted. This can result in a flicker due to flag changes at
24
+ * startup time.
25
+ *
26
+ * This component can be used as a standalone provider. However, be mindful to only include the component once
27
+ * within your application. This provider is used inside the `withFbProviderHOC` and can be used instead to initialize
28
+ * the `@featbit/js-client-sdk`. For async initialization, check out the `asyncWithFbProvider` function
29
+ */
30
+ class FbProvider extends React.Component<ProviderConfig, FbHocState> implements EnhancedComponent {
31
+ readonly state: Readonly<FbHocState>;
32
+ bootstrapFlags: IFlagSet;
33
+
34
+ constructor(props: ProviderConfig) {
35
+ super(props);
36
+
37
+ const {options} = props;
38
+ this.bootstrapFlags = (options?.bootstrap || []).reduce((acc: {[key: string]: string}, flag: any) => {
39
+ acc[flag.id] = flag.variation;
40
+ return acc;
41
+ }, {} as {[key: string]: string});;
42
+
43
+ this.state = {
44
+ flags: {},
45
+ unproxiedFlags: {},
46
+ flagKeyMap: {},
47
+ fbClient: undefined,
48
+ };
49
+
50
+ if (options?.bootstrap && options?.bootstrap.length > 0) {
51
+ const {useCamelCaseFlagKeys} = this.getReactOptions();
52
+ const flags = useCamelCaseFlagKeys ? camelCaseKeys(this.bootstrapFlags) : this.bootstrapFlags;
53
+ this.state = {
54
+ flags,
55
+ unproxiedFlags: flags,
56
+ flagKeyMap: {},
57
+ fbClient: undefined,
58
+ };
59
+ }
60
+ }
61
+
62
+ getReactOptions = () => ({...defaultReactOptions, ...this.props.reactOptions});
63
+
64
+ subscribeToChanges = (fbClient: IFbClient) => {
65
+ fbClient.on('update', (changedKeys: string[]) => {
66
+ const updates: IFlagSet = changedKeys.reduce((acc, key) => {
67
+ acc[key] = fbClient.variation(key, '');
68
+ return acc;
69
+ }, {} as IFlagSet);
70
+
71
+ const unproxiedFlags = {
72
+ ...this.state.unproxiedFlags,
73
+ ...updates,
74
+ };
75
+
76
+ if (Object.keys(updates).length > 0) {
77
+ this.setState({
78
+ unproxiedFlags,
79
+ ...getFlagsProxy(fbClient, this.bootstrapFlags, unproxiedFlags, this.getReactOptions())
80
+ })
81
+ }
82
+ });
83
+ };
84
+
85
+ init = async () => {
86
+ const {options} = this.props;
87
+ let client: IFbClient = this.props.fbClient!;
88
+ const reactOptions = this.getReactOptions();
89
+ let unproxiedFlags;
90
+ if (client) {
91
+ unproxiedFlags = await fetchFlags(client);
92
+ } else {
93
+ const initialisedOutput = await initClient(reactOptions, options);
94
+ unproxiedFlags = initialisedOutput.flags;
95
+ client = initialisedOutput.fbClient!;
96
+ }
97
+
98
+ this.setState({
99
+ unproxiedFlags,
100
+ ...getFlagsProxy(client, this.bootstrapFlags, unproxiedFlags, reactOptions),
101
+ fbClient: client
102
+ });
103
+
104
+ this.subscribeToChanges(client);
105
+ };
106
+
107
+ async componentDidMount() {
108
+ const {options, deferInitialization} = this.props;
109
+ if (deferInitialization && !options) {
110
+ return;
111
+ }
112
+
113
+ await this.init();
114
+ }
115
+
116
+ async componentDidUpdate(prevProps: ProviderConfig) {
117
+ const {options, deferInitialization} = this.props;
118
+ const userJustLoaded = !prevProps.options?.user && options?.user;
119
+ if (deferInitialization && userJustLoaded) {
120
+ await this.init();
121
+ }
122
+ }
123
+
124
+ render() {
125
+ const {flags, flagKeyMap, fbClient} = this.state;
126
+
127
+ // Conditional rendering when fbClient is null
128
+ if (fbClient === undefined) {
129
+ return null; // or Loading Indicator or any other placeholder
130
+ }
131
+
132
+ return <Provider value={{ flags, flagKeyMap, fbClient }}>{ this.props.children }</Provider>;
133
+ }
134
+ }
135
+
136
+ export default FbProvider;
package/src/types.ts ADDED
@@ -0,0 +1,103 @@
1
+ import { IFbClient, IOptions, FlagValue } from '@featbit/js-client-sdk';
2
+
3
+ export interface IFlagSet {
4
+ [key: string]: FlagValue;
5
+ }
6
+
7
+ /**
8
+ * Initialization options for the FeatBit React SDK. These are in addition to the options exposed
9
+ * by [[IOption]] which are common to both the JavaScript and React SDKs.
10
+ */
11
+ export interface FbReactOptions {
12
+ /**
13
+ * Whether the React SDK should transform flag keys into camel-cased format.
14
+ * Using camel-cased flag keys allow for easier use as prop values, however,
15
+ * these keys won't directly match the flag keys as known to LaunchDarkly.
16
+ * Consequently, flag key collisions may be possible and the Code References feature
17
+ * will not function properly.
18
+ *
19
+ * This is false by default, if set to true, keys will automatically be converted to camel-case.
20
+ */
21
+ useCamelCaseFlagKeys?: boolean;
22
+
23
+ /**
24
+ * Whether to send flag evaluation events when a flag is read from the `flags` object
25
+ * returned by the `useFlags` hook. This is true by default, meaning flag evaluation
26
+ * events will be sent by default.
27
+ */
28
+ sendEventsOnFlagRead?: boolean;
29
+ }
30
+
31
+ /**
32
+ * Contains default values for the `reactOptions` object.
33
+ */
34
+ export const defaultReactOptions = {useCamelCaseFlagKeys: false, sendEventsOnFlagRead: true};
35
+
36
+ /**
37
+ * Configuration object used to initialise FeatBit's JS client.
38
+ */
39
+ export interface ProviderConfig {
40
+ /**
41
+ * If set to true, the FeatBit will not be initialized until the option prop has been defined.
42
+ */
43
+ deferInitialization?: boolean;
44
+
45
+ /**
46
+ * FeatBit initialization options. These options are common between FeatBit's JavaScript and React SDKs.
47
+ */
48
+ options?: IOptions;
49
+
50
+ /**
51
+ * Additional initialization options specific to the React SDK.
52
+ *
53
+ * @see options
54
+ */
55
+ reactOptions?: FbReactOptions;
56
+
57
+ /**
58
+ * Optionally, the FB can be initialised outside the provider
59
+ * and passed in, instead of being initialised by the provider.
60
+ * Note: it should only be passed in when it has emitted the 'ready'
61
+ * event, to ensure that the flags are properly set.
62
+ */
63
+ fbClient?: IFbClient;
64
+ }
65
+
66
+ /**
67
+ * The return type of withFbProvider HOC. Exported for testing purposes only.
68
+ *
69
+ * @ignore
70
+ */
71
+ export interface EnhancedComponent extends React.Component {
72
+ subscribeToChanges(fbClient: IFbClient): void;
73
+
74
+ // tslint:disable-next-line:invalid-void
75
+ componentDidMount(): Promise<void>;
76
+
77
+ // tslint:disable-next-line:invalid-void
78
+ componentDidUpdate(prevProps: ProviderConfig): Promise<void>;
79
+ }
80
+
81
+ /**
82
+ * Return type of `initClient`.
83
+ */
84
+ export interface AllFlagsFbClient {
85
+ /**
86
+ * Contains all flags from FeatBit.
87
+ */
88
+ flags: IFlagSet;
89
+
90
+ /**
91
+ * An instance of `FB` from the FeatBit JS SDK.
92
+ */
93
+ fbClient: IFbClient;
94
+ }
95
+
96
+ /**
97
+ * Map of camelized flag keys to original unmodified flag keys.
98
+ */
99
+ export interface FlagKeyMap {
100
+ [camelCasedKey: string]: string;
101
+ }
102
+
103
+ export * from '@featbit/js-client-sdk';
@@ -0,0 +1,21 @@
1
+ import { useContext } from 'react';
2
+ import context, { FbContext } from './context';
3
+ import { IFbClient } from "@featbit/js-client-sdk";
4
+
5
+ // tslint:disable:max-line-length
6
+ /**
7
+ * `useFbClient` is a custom hook which returns the underlying [FeatBit JavaScript SDK client object](https://github.com/featbit/featbit-js-client-sdk).
8
+ * Like the `useFlags` custom hook, `useFbClient` also uses the `useContext` primitive to access the FeatBit
9
+ * context set up by `withFbProvider`. You will still need to use the `withFbProvider` HOC
10
+ * to initialise the react sdk to use this custom hook.
11
+ *
12
+ * @return The `@featbit/js-client-sdk` `FB` object
13
+ */
14
+ // tslint:enable:max-line-length
15
+ const useFbClient = (): IFbClient => {
16
+ const { fbClient} = useContext<FbContext>(context);
17
+
18
+ return fbClient!;
19
+ };
20
+
21
+ export default useFbClient;
@@ -0,0 +1,19 @@
1
+ import { useContext } from 'react';
2
+ import context, { FbContext } from './context';
3
+ import { IFlagSet } from "./types";
4
+
5
+ /**
6
+ * `useFlags` is a custom hook which returns all feature flags. It uses the `useContext` primitive
7
+ * to access the FeatBit context set up by `withFbProvider`. As such you will still need to
8
+ * use the `withFbProvider` HOC at the root of your app to initialize the React SDK and populate the
9
+ * context with `fbClient` and your flags.
10
+ *
11
+ * @return All the feature flags configured in FeatBit
12
+ */
13
+ const useFlags = <T extends IFlagSet = IFlagSet>(): T => {
14
+ const {flags} = useContext<FbContext>(context);
15
+
16
+ return flags as T;
17
+ };
18
+
19
+ export default useFlags;
package/src/utils.ts ADDED
@@ -0,0 +1,39 @@
1
+ import { IEvalDetail, IFbClient } from '@featbit/js-client-sdk';
2
+ import camelCase from 'lodash.camelcase';
3
+ import { IFlagSet } from "./types";
4
+
5
+ /**
6
+ * Transforms a set of flags so that their keys are camelCased. This function ignores
7
+ * flag keys which start with `$`.
8
+ *
9
+ * @param rawFlags A mapping of flag keys and their values
10
+ * @return A transformed `IFeatureFlagSet` with camelCased flag keys
11
+ */
12
+ export const camelCaseKeys = (rawFlags: IFlagSet) => {
13
+ const flags: IFlagSet = {};
14
+ for (const rawFlag in rawFlags) {
15
+ // Exclude system keys
16
+ if (rawFlag.indexOf('$') !== 0) {
17
+ flags[camelCase(rawFlag)] = rawFlags[rawFlag]; // tslint:disable-line:no-unsafe-any
18
+ }
19
+ }
20
+
21
+ return flags;
22
+ };
23
+
24
+ /**
25
+ * Retrieves flag values.
26
+ *
27
+ * @param fbClient FeatBit client
28
+ *
29
+ * @returns an `IFeatureFlagSet` with the current flag values from FeatBit
30
+ */
31
+ export const fetchFlags = async (
32
+ fbClient: IFbClient
33
+ ) => {
34
+ const evalDetails: IEvalDetail<string>[] = await fbClient.getAllVariations();
35
+
36
+ return evalDetails.map(({flagKey, value}) => ({[flagKey]: value}));
37
+ };
38
+
39
+ export default {camelCaseKeys, fetchFlags};
@@ -0,0 +1,58 @@
1
+ import * as React from 'react';
2
+ import { Consumer, FbContext } from './context';
3
+ import { IFbClient } from '@featbit/js-client-sdk';
4
+ import { IFlagSet } from "./types";
5
+
6
+ /**
7
+ * Controls the props the wrapped component receives from the `FbConsumer` HOC.
8
+ */
9
+ export interface ConsumerOptions {
10
+ /**
11
+ * If true then the wrapped component only receives the `fbClient` instance
12
+ * and nothing else.
13
+ */
14
+ clientOnly: boolean;
15
+ }
16
+
17
+ /**
18
+ * The possible props the wrapped component can receive from the `FbConsumer` HOC.
19
+ */
20
+ export interface FbProps {
21
+ /**
22
+ * A map of feature flags from their keys to their values.
23
+ * Keys are camelCased using `lodash.camelcase`.
24
+ */
25
+ flags?: IFlagSet;
26
+
27
+ /**
28
+ * An instance of `FB` from the FeatBit JS Client SDK (`@featbit/js-client-sdk`)
29
+ */
30
+ fbClient?: IFbClient;
31
+ }
32
+
33
+ /**
34
+ * withFbConsumer is a function which accepts an optional options object and returns a function
35
+ * which accepts your React component. This function returns a HOC with flags
36
+ * and the FB instance injected via props.
37
+ *
38
+ * @param options - If you need only the `fbClient` instance and not flags, then set `{ clientOnly: true }`
39
+ * to only pass the fbClient prop to your component. Defaults to `{ clientOnly: false }`.
40
+ * @return A HOC with flags and the `fbClient` instance injected via props
41
+ */
42
+ function withFbConsumer(options: ConsumerOptions = {clientOnly: false}) {
43
+ return function withFbConsumerHoc<P>(WrappedComponent: React.ComponentType<P & FbProps>) {
44
+ return (props: P) => (
45
+ <Consumer>
46
+ { ({flags, fbClient}: FbContext) => {
47
+ if (options.clientOnly) {
48
+ return <WrappedComponent fbClient={ fbClient } { ...props } />;
49
+ }
50
+
51
+ return <WrappedComponent flags={ flags } fbClient={ fbClient } { ...props } />;
52
+ } }
53
+ </Consumer>
54
+ );
55
+ };
56
+ }
57
+
58
+ export default withFbConsumer;
@@ -0,0 +1,49 @@
1
+ import * as React from 'react';
2
+ import { defaultReactOptions, ProviderConfig } from './types';
3
+ import FbProvider from './provider';
4
+ import hoistNonReactStatics from 'hoist-non-react-statics';
5
+ import IntrinsicAttributes = React.JSX.IntrinsicAttributes;
6
+
7
+ /**
8
+ * `withFbProvider` is a function which accepts a config object which is used to
9
+ * initialize `@featbit/js-client-sdk`.
10
+ *
11
+ * This HOC handles passing configuration to the `FbProvider`, which does the following:
12
+ * - It initializes the fbClient instance by calling `@featbit/js-client-sdk` init on `componentDidMount`
13
+ * - It saves all flags and the fbClient instance in the context API
14
+ * - It subscribes to flag changes and propagate them through the context API
15
+ *
16
+ * The difference between `withFbProvider` and `asyncWithFbProvider` is that `withFbProvider` initializes
17
+ * `@featbit/js-client-sdk` at `componentDidMount`. This means your flags and the fbClient are only available after
18
+ * your app has mounted. This can result in a flicker due to flag changes at startup time.
19
+ *
20
+ * `asyncWithFbProvider` initializes `@featbit/js-client-sdk` at the entry point of your app prior to render.
21
+ * This means that your flags and the fbClient are ready at the beginning of your app. This ensures your app does not
22
+ * flicker due to flag changes at startup time.
23
+ *
24
+ * @param config - The configuration used to initialize FeatBit JS Client SDK
25
+ * @return A function which accepts your root React component and returns a HOC
26
+ */
27
+ export function withFbProvider<T extends IntrinsicAttributes = {}>(
28
+ config: ProviderConfig,
29
+ ): (WrappedComponent: React.ComponentType<T>) => any {
30
+ return function withFbProviderHoc(WrappedComponent: React.ComponentType<T>): any {
31
+ const {reactOptions: userReactOptions} = config;
32
+ const reactOptions = {...defaultReactOptions, ...userReactOptions};
33
+ const providerProps = {...config, reactOptions};
34
+
35
+ function HoistedComponent(props: T) {
36
+ return (
37
+ <FbProvider { ...providerProps }>
38
+ <WrappedComponent { ...props } />
39
+ </FbProvider>
40
+ );
41
+ }
42
+
43
+ hoistNonReactStatics(HoistedComponent, WrappedComponent);
44
+
45
+ return HoistedComponent;
46
+ };
47
+ }
48
+
49
+ export default withFbProvider;