vwo-fme-react-sdk 0.1.0 → 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.
package/CHANGELOG.md CHANGED
@@ -5,7 +5,24 @@ All notable changes to this project will be documented in this file.
5
5
  The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
6
  and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
7
 
8
- ## [1.0.0] - 2025-02-13
8
+ ## [1.0.0] - 2025-04-12
9
+
10
+ ### Changed
11
+
12
+ - Stable Release of VWO FME React SDK
13
+
14
+ ### Fixed
15
+
16
+ - Update dependency of `useMemo` hook inside `useGetFlag` hook
17
+
18
+ ## [0.2.0] - 2025-04-11
19
+
20
+ ### Added
21
+
22
+ - Ensure compatibility with older versions that do not support optional chaining.
23
+ - Allow user context to be optionally passed to the `useGetFlag` hook, enabling it to override the context provided to the `VWOProvider` hook.
24
+
25
+ ## [0.1.0] - 2025-02-13
9
26
 
10
27
  ### Added
11
28
 
@@ -13,7 +30,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
13
30
 
14
31
  #### Basic Usage
15
32
 
16
- ```javascript
33
+ ```typescript
17
34
  import React from 'react';
18
35
  import { VWOProvider } from 'vwo-fme-react-sdk';
19
36
 
@@ -34,7 +51,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
34
51
  };
35
52
 
36
53
  const App = () => (
37
- <VWOProvider config={vwoConfig} context={userContext}>
54
+ <VWOProvider config={vwoConfig} userContext={userContext}>
38
55
  <YourComponent />
39
56
  </VWOProvider>
40
57
  );
package/README.md CHANGED
@@ -47,7 +47,7 @@ const userContext = {
47
47
  };
48
48
 
49
49
  const App = () => (
50
- <VWOProvider config={vwoConfig} context={userContext}>
50
+ <VWOProvider config={vwoConfig} userContext={userContext}>
51
51
  <YourComponent />
52
52
  </VWOProvider>
53
53
  );
@@ -55,6 +55,49 @@ const App = () => (
55
55
  export default App;
56
56
  ```
57
57
 
58
+ Inside your component, use feature flagging and experimentation
59
+
60
+ ```javascript
61
+ // Import hooks
62
+ import { useGetFlag, useGetFlagVariable } from 'vwo-fme-react-sdk';
63
+
64
+ const YourComponent = () => {
65
+ // Retrieve the flag using the feature key
66
+ const { flag, isReady } = useGetFlag('feature_key');
67
+
68
+ // Or, pass userContext, if not provided at the time of using VWOProvider or you want to use updated user context
69
+ // const { flag, isReady } = useGetFlag('feature_key', userContext);
70
+
71
+ if (!isReady()) {
72
+ return <div>Default/Zero state</div>;
73
+ }
74
+ // Check if the flag is enabled
75
+ const isEnabled = flag?.isEnabled();
76
+ if (isEnabled) {
77
+ // Use the flag object returned by useGetFlag to retrieve a specific variable
78
+ // Replace 'variableKey' with the actual key for the variable you want to retrieve
79
+ const variableKey = 'variable_name'; // Replace with actual variable key
80
+ const variableValue = useGetFlagVariable(flag, variableKey, 'default_value');
81
+
82
+ // Display the feature flag variable value
83
+ return (
84
+ <div>
85
+ <p>Feature Flag Variable Value: {variableValue}</p>
86
+ </div>
87
+ );
88
+ }
89
+
90
+ // Display a message if the feature is not enabled
91
+ return (
92
+ <div>
93
+ <p>Feature is not enabled!</p>
94
+ </div>
95
+ );
96
+ };
97
+
98
+ export default YourComponent;
99
+ ```
100
+
58
101
  ## Advanced Configuration Options
59
102
 
60
103
  To customize the SDK further, additional parameters can be passed to the `VWOProvider` component. Here’s a table describing each option:
@@ -102,9 +145,10 @@ Feature Flags serve as the foundation for all testing, personalization, and roll
102
145
  To implement a feature flag, first use the `useGetFlag` hook to retrieve the flag configuration.
103
146
  The `useGetFlag` hook provides a simple way to check if a feature is enabled for a specific user and access its variables. It returns a feature flag object that contains methods for checking the feature's status and retrieving any associated variables.
104
147
 
105
- | Parameter | Description | Required | Type | Example |
106
- | ------------ | ------------------------------------- | -------- | ------ | ---------------- |
107
- | `featureKey` | Unique identifier of the feature flag | Yes | String | `'new_checkout'` |
148
+ | Parameter | Description | Required | Type | Example |
149
+ | ------------ | -------------------------------------------------------------- | -------- | ------ | ------------------------- |
150
+ | `featureKey` | Unique identifier of the feature flag | Yes | String | `'new_checkout'` |
151
+ | `context` | User Context to be passed, if not at the time of `VWOProvider` | No | Object | `{ id: 'unique_user_id'}` |
108
152
 
109
153
  Example usage:
110
154
 
@@ -114,8 +158,14 @@ import { useGetFlag, useGetFlagVariable } from 'vwo-fme-react-sdk'; // Import ho
114
158
 
115
159
  const YourComponent = () => {
116
160
  // Retrieve the flag using the feature key
117
- const flag = useGetFlag('feature_key');
161
+ const { flag, isReady } = useGetFlag('feature_key');
118
162
 
163
+ // Or, pass userContext, if not provided at the time of using VWOProvider or you want to use updated user context
164
+ // const { flag, isReady } = useGetFlag('feature_key', userContext);
165
+
166
+ if (!isReady()) {
167
+ return <div>Default/Zero state</div>;
168
+ }
119
169
  // Check if the flag is enabled
120
170
  const isEnabled = flag?.isEnabled();
121
171
  if (isEnabled) {
@@ -202,7 +252,7 @@ const vwoConfig = {
202
252
  };
203
253
 
204
254
  const App = () => (
205
- <VWOProvider config={vwoConfig} context={{ id: 'unique_user_id' }}>
255
+ <VWOProvider config={vwoConfig} userContext={{ id: 'unique_user_id' }}>
206
256
  <YourComponent />
207
257
  </VWOProvider>
208
258
  );
@@ -260,7 +310,7 @@ const vwoConfig = {
260
310
  };
261
311
 
262
312
  const App = () => (
263
- <VWOProvider config={vwoConfig} context={{ id: 'unique_user_id' }}>
313
+ <VWOProvider config={vwoConfig} userContext={{ id: 'unique_user_id' }}>
264
314
  <YourComponent />
265
315
  </VWOProvider>
266
316
  );
@@ -290,7 +340,7 @@ const options = {
290
340
  };
291
341
 
292
342
  const App = () => (
293
- <VWOProvider config={vwoConfig} context={{ id: 'unique_user_id' }}>
343
+ <VWOProvider config={vwoConfig} userContext={{ id: 'unique_user_id' }}>
294
344
  <YourComponent />
295
345
  </VWOProvider>
296
346
  );
@@ -311,7 +361,7 @@ const options = {
311
361
  };
312
362
 
313
363
  const App = () => (
314
- <VWOProvider config={vwoConfig} context={{ id: 'unique_user_id' }}>
364
+ <VWOProvider config={vwoConfig} userContext={{ id: 'unique_user_id' }}>
315
365
  <YourComponent />
316
366
  </VWOProvider>
317
367
  );
@@ -13,12 +13,5 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- /**
17
- * Context for VWO SDK
18
- */
19
16
  export declare const VWOContext: any;
20
- /**
21
- * Hook to use the VWO context
22
- * @returns VWO context
23
- */
24
17
  export declare const useVWOContext: () => any;
@@ -13,23 +13,22 @@
13
13
  * See the License for the specific language governing permissions and
14
14
  * limitations under the License.
15
15
  */
16
- import { ReactNode } from 'react';
17
- /**
18
- * Props for VWOProvider
19
- */
16
+ import React, { ReactNode } from 'react';
20
17
  interface VWOProviderProps {
21
18
  client?: any;
22
19
  config?: any;
23
- context: any;
20
+ userContext?: any;
24
21
  children: ReactNode;
25
22
  }
26
23
  /**
27
- * VWOProvider component to provide the VWO SDK instance and context to the app
28
- * @param client - VWO SDK instance
29
- * @param config - VWO SDK config (initialization config)
30
- * @param context - VWO SDK context (userContext)
31
- * @param children - React children (ReactNode)
32
- * @returns VWOProvider component
24
+ * VWOProvider component to provide VWO client and configuration context to child components.
25
+ *
26
+ * @param {Object} props - The properties for the VWOProvider component.
27
+ * @param {Object} props.client - The VWO client instance.
28
+ * @param {Object} props.config - Configuration settings for the VWO client.
29
+ * @param {Object} props.userContext - Initial user context for the VWO client.
30
+ * @param {React.ReactNode} props.children - Child components that will have access to the VWO context.
31
+ * @returns {JSX.Element} The provider component wrapping its children with VWO context.
33
32
  */
34
- export declare const VWOProvider: ({ client, config, context, children }: VWOProviderProps) => any;
33
+ export declare const VWOProvider: React.FC<VWOProviderProps>;
35
34
  export {};
package/dist/index.d.ts CHANGED
@@ -20,4 +20,5 @@ export { useGetFlagVariable } from './useGetFlagVariable';
20
20
  export { useGetFlagVariables } from './useGetFlagVariable';
21
21
  export { useTrackEvent } from './useTrackEvent';
22
22
  export { useSetAttribute } from './useSetAttribute';
23
+ export { useVWOContext } from './VWOContext';
23
24
  export { init } from 'vwo-fme-node-sdk';
@@ -14,8 +14,16 @@
14
14
  * limitations under the License.
15
15
  */
16
16
  /**
17
- * Hook to get a feature flag value
18
- * @param featureKey - The key of the feature flag to get
19
- * @returns The feature flag value
17
+ * Custom hook to retrieve a feature flag using VWO client.
18
+ *
19
+ * @param {string} featureKey - The key of the feature flag to retrieve.
20
+ * @param {Object} [context] - Optional user context to use for fetching the flag.
21
+ * @returns {Object} An object containing the flag and a readiness status.
20
22
  */
21
- export declare const useGetFlag: (featureKey: string) => any;
23
+ export declare const useGetFlag: (
24
+ featureKey: string,
25
+ context?: any,
26
+ ) => {
27
+ flag: any;
28
+ isReady: () => boolean;
29
+ };
@@ -523,17 +523,11 @@ function getLogger() {
523
523
  * See the License for the specific language governing permissions and
524
524
  * limitations under the License.
525
525
  */
526
- /**
527
- * Context for VWO SDK
528
- */
529
526
  const VWOContext = /*#__PURE__*/React.createContext({
530
527
  vwoClient: null,
531
- userContext: {}
528
+ userContext: null,
529
+ setUserContext: undefined
532
530
  });
533
- /**
534
- * Hook to use the VWO context
535
- * @returns VWO context
536
- */
537
531
  const useVWOContext = () => {
538
532
  const logger = getLogger();
539
533
  try {
@@ -567,69 +561,69 @@ const useVWOContext = () => {
567
561
  * limitations under the License.
568
562
  */
569
563
  /**
570
- * VWOProvider component to provide the VWO SDK instance and context to the app
571
- * @param client - VWO SDK instance
572
- * @param config - VWO SDK config (initialization config)
573
- * @param context - VWO SDK context (userContext)
574
- * @param children - React children (ReactNode)
575
- * @returns VWOProvider component
564
+ * VWOProvider component to provide VWO client and configuration context to child components.
565
+ *
566
+ * @param {Object} props - The properties for the VWOProvider component.
567
+ * @param {Object} props.client - The VWO client instance.
568
+ * @param {Object} props.config - Configuration settings for the VWO client.
569
+ * @param {Object} props.userContext - Initial user context for the VWO client.
570
+ * @param {React.ReactNode} props.children - Child components that will have access to the VWO context.
571
+ * @returns {JSX.Element} The provider component wrapping its children with VWO context.
576
572
  */
577
573
  const VWOProvider = ({
578
574
  client,
579
575
  config,
580
- context,
576
+ userContext,
581
577
  children
582
578
  }) => {
583
579
  const [vwoClient, setVwoClient] = React.useState(client || null);
584
- const vwoClientRef = React.useRef(vwoClient);
585
- const isMounted = React.useRef(true);
580
+ const [context, setContext] = React.useState(userContext || null);
581
+ const vwoClientRef = React.useRef(vwoClient); // Store the client reference without triggering re-renders
582
+ const isMounted = React.useRef(true); // Prevent updates after unmount
583
+ const logger = getLogger();
586
584
  // Initialize logger globally before using it
587
585
  React.useEffect(() => {
588
- if (config?.logger) {
586
+ if (config != null && config.logger) {
589
587
  initLogger(config);
590
588
  }
591
589
  }, [config]);
592
- const logger = getLogger();
593
- // Initialize the VWO SDK instance when the component mounts
590
+ // Initialize the VWO SDK instance only once when the component mounts or if config is updated
594
591
  React.useEffect(() => {
592
+ // If neither vwoClient nor config is provided, log the error
595
593
  if (!vwoClient && !config) {
596
594
  logger.error("VWOProvider Error: Either `client` or `config` must be provided.");
597
595
  return;
598
596
  }
599
- if (!context || !isObject(context)) {
600
- logger.error("VWOProvider Error: `context` is required and must be a valid object.");
601
- return;
602
- }
603
- isMounted.current = true;
604
- async function initializeVWO() {
605
- if (!vwoClient && config) {
606
- try {
607
- // Initialize the VWO SDK instance
597
+ const initializeVWO = async () => {
598
+ try {
599
+ if (!vwoClient && config) {
600
+ // Initialize the VWO SDK instance if vwoClient is not already initialized
608
601
  const instance = await vwoFmeNodeSdk.init(config);
609
602
  if (isMounted.current) {
610
- // Update the VWO SDK instance
611
603
  setVwoClient(instance);
612
- // Update the ref with the new instance
613
604
  vwoClientRef.current = instance;
614
605
  }
615
- } catch (error) {
616
- logger.error("VWO-SDK Initialization failed:");
617
606
  }
607
+ } catch (error) {
608
+ logger.error(`VWO-SDK Initialization failed: ${error}`);
618
609
  }
610
+ };
611
+ // Only initialize once
612
+ if (!vwoClient && config) {
613
+ initializeVWO();
619
614
  }
620
- // Initialize the VWO SDK instance
621
- initializeVWO();
622
- // Cleanup the VWO SDK instance when the component unmounts
623
615
  return () => {
624
616
  isMounted.current = false;
625
- if (vwoClientRef.current?.destroy) vwoClientRef.current.destroy();
617
+ if (vwoClientRef.current && vwoClientRef.current.destroy) {
618
+ vwoClientRef.current.destroy();
619
+ }
626
620
  };
627
- }, [vwoClient, config]);
628
- // Provide the VWO SDK instance and context to the app
621
+ }, [config, vwoClient]); // Re-run only when config or vwoClient changes
629
622
  return React__default.createElement(VWOContext.Provider, {
630
623
  value: {
631
624
  vwoClient,
632
- userContext: context
625
+ userContext: context,
626
+ setUserContext: setContext
633
627
  }
634
628
  }, children);
635
629
  };
@@ -686,59 +680,79 @@ const useVWOClient = () => {
686
680
  * limitations under the License.
687
681
  */
688
682
  /**
689
- * Hook to get a feature flag value
690
- * @param featureKey - The key of the feature flag to get
691
- * @returns The feature flag value
683
+ * Custom hook to retrieve a feature flag using VWO client.
684
+ *
685
+ * @param {string} featureKey - The key of the feature flag to retrieve.
686
+ * @param {Object} [context] - Optional user context to use for fetching the flag.
687
+ * @returns {Object} An object containing the flag and a readiness status.
692
688
  */
693
- const useGetFlag = featureKey => {
694
- // This is the return schema for the flag if the feature key is not found or the flag is not enabled
689
+ const useGetFlag = (featureKey, context) => {
690
+ // Define the error return schema for the hook
695
691
  const errorReturnSchema = {
696
- isEnabled: () => false,
697
- getVariables: () => [],
698
- getVariable: (_key, defaultValue) => defaultValue
692
+ flag: {
693
+ isEnabled: () => false,
694
+ getVariables: () => [],
695
+ getVariable: (_key, defaultValue) => defaultValue
696
+ },
697
+ isReady: () => false
699
698
  };
699
+ // Destructure vwoClient, setUserContext, and userContext from VWOContext
700
+ const {
701
+ vwoClient,
702
+ setUserContext,
703
+ userContext
704
+ } = useVWOContext();
705
+ // State to store the flag and loading status
706
+ const [flag, setFlag] = React.useState(null);
707
+ const [isLoading, setIsLoading] = React.useState(true);
708
+ // Get the logger instance
700
709
  const logger = getLogger();
701
- try {
710
+ // Memoize the user context to prevent unnecessary re-renders
711
+ const stableUserContext = React.useMemo(() => {
712
+ return context || userContext || {};
713
+ }, [JSON.stringify(context || userContext || {})]); // Only recreate if userContext actually changes
714
+ // Define the getFlag function to fetch the feature flag
715
+ const getFlag = React.useCallback(async () => {
716
+ // Check if featureKey is provided
702
717
  if (!featureKey) {
703
718
  logger.error('Feature key is required for useGetFlag hook');
704
719
  return errorReturnSchema;
705
720
  }
706
- const [flag, setFlag] = React.useState(null);
707
- const {
708
- vwoClient,
709
- userContext
710
- } = useVWOContext();
711
- if (!vwoClient) {
712
- logger.error('VWO Client is missing in useGetFlag hook. Ensure VWOProvider is correctly initialized.');
721
+ // Check if the user context is a valid object
722
+ if (!isObject(stableUserContext)) {
723
+ logger.error('Invalid user context in useGetFlag hook');
713
724
  return errorReturnSchema;
714
725
  }
715
- if (!userContext || !isObject(userContext)) {
716
- logger.error('Invalid user context in useGetFlag hook. Ensure a valid userContext is provided.');
717
- return errorReturnSchema;
726
+ // Try to fetch the feature flag and handle errors
727
+ try {
728
+ setIsLoading(true);
729
+ const result = await vwoClient.getFlag(featureKey, stableUserContext);
730
+ setFlag(result);
731
+ // Set the user context in the context to ensure it's available for other hooks
732
+ setUserContext(stableUserContext);
733
+ } catch (error) {
734
+ logger.error(`Error fetching feature flag "${featureKey}": ${error}`);
735
+ setFlag(null);
736
+ } finally {
737
+ setIsLoading(false);
718
738
  }
719
- // Memoize the userContext to avoid unnecessary re-fetching
720
- const stableUserContext = React.useMemo(() => userContext, [userContext]);
721
- // Memoize the getFlag function to avoid re-creation
722
- const getFlag = React.useMemo(() => {
723
- return async () => {
724
- try {
725
- const result = await vwoClient.getFlag(featureKey, stableUserContext);
726
- setFlag(result);
727
- } catch (error) {
728
- logger.error(`Error fetching feature flag "${featureKey}": ${error}`);
729
- setFlag({});
730
- }
731
- };
732
- }, [featureKey, vwoClient, stableUserContext]);
733
- // Runs only when `getFlag` reference changes
734
- React.useEffect(() => {
735
- getFlag();
736
- }, [getFlag]);
737
- return flag;
738
- } catch (error) {
739
- logger.error(`Error getting feature flag: ${error}`);
739
+ }, [featureKey, stableUserContext, vwoClient]);
740
+ // Ensure vwoClient is ready
741
+ if (!vwoClient) {
742
+ logger.error('VWO Client is missing in useGetFlag hook. Ensure VWOProvider is correctly initialized.');
740
743
  return errorReturnSchema;
741
744
  }
745
+ // Run effect when dependencies change to fetch the flag
746
+ React.useEffect(() => {
747
+ if (featureKey && vwoClient && stableUserContext) {
748
+ getFlag();
749
+ }
750
+ }, [featureKey, stableUserContext, vwoClient]);
751
+ // Return the flag and readiness status
752
+ return {
753
+ flag,
754
+ isReady: () => !isLoading && !!flag
755
+ };
742
756
  };
743
757
 
744
758
  /**
@@ -877,6 +891,10 @@ const useSetAttribute = attributeMap => {
877
891
  vwoClient,
878
892
  userContext
879
893
  } = useVWOContext();
894
+ if (!vwoClient) {
895
+ logger.error('VWO Client is missing in useSetAttribute hook. Ensure VWOProvider is correctly initialized.');
896
+ return {};
897
+ }
880
898
  if (!userContext || !isObject(userContext)) {
881
899
  logger.error('Invalid user context in useSetAttribute hook. Ensure a valid userContext is provided.');
882
900
  return {};
@@ -901,4 +919,5 @@ exports.useGetFlagVariables = useGetFlagVariables;
901
919
  exports.useSetAttribute = useSetAttribute;
902
920
  exports.useTrackEvent = useTrackEvent;
903
921
  exports.useVWOClient = useVWOClient;
922
+ exports.useVWOContext = useVWOContext;
904
923
  //# sourceMappingURL=vwo-fme-react-sdk.cjs.development.js.map