@ninetailed/experience.js-react 7.11.0 → 7.11.1

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/index.cjs.js CHANGED
@@ -99,32 +99,49 @@ function __rest(s, e) {
99
99
  return t;
100
100
  }
101
101
 
102
+ function formatProfileForHook(profile) {
103
+ const profileStateWithoutExperiences = __rest(profile, ["experiences"]);
104
+ return Object.assign(Object.assign({}, profileStateWithoutExperiences), {
105
+ loading: profile.status === 'loading'
106
+ });
107
+ }
108
+ /**
109
+ * Custom hook that provides access to the Ninetailed profile state
110
+ * with the 'experiences' property removed to prevent unnecessary re-renders.
111
+ *
112
+ * This hook handles profile state changes efficiently by:
113
+ * 1. Only updating state when actual changes occur
114
+ * 2. Removing the large 'experiences' object from the state
115
+ * 3. Properly cleaning up subscriptions when components unmount
116
+ *
117
+ * @returns The profile state without the 'experiences' property
118
+ */
102
119
  const useProfile = () => {
103
120
  const ninetailed = useNinetailed();
104
- const [profileState, setProfileState] = React.useState(ninetailed.profileState);
105
- const profileStateRef = React.useRef({});
106
- /**
107
- * This effect compares the old and new profile state before updating it.
108
- * We use a ref to avoid an infinite loop which can happen when an empty profile state was updated with no changes.
109
- * This behaviour occurred as the validation handling on the error property was not set properly in the "CreateProfile" and "UpdateProfile" endpoint types.
110
- * Furthermore, it was also observed, that it "only" occurred when the preview plugin was used in parallel.
111
- */
121
+ const [strippedProfileState, setStrippedProfileState] = React.useState(formatProfileForHook(ninetailed.profileState));
122
+ // Reference to track the previous profile state for comparison
123
+ const profileStateRef = React.useRef(ninetailed.profileState);
112
124
  React.useEffect(() => {
113
- ninetailed.onProfileChange(profileState => {
114
- if (radash.isEqual(profileState, profileStateRef.current)) {
115
- experience_jsShared.logger.debug('Profile State Did Not Change', profileState);
125
+ const unsubscribe = ninetailed.onProfileChange(changedProfileState => {
126
+ // Skip update if the profile hasn't actually changed
127
+ // Here we compare the entire profile including experiences and changes
128
+ if (radash.isEqual(changedProfileState, profileStateRef.current)) {
129
+ experience_jsShared.logger.debug('Profile State Did Not Change', changedProfileState);
116
130
  return;
117
- } else {
118
- setProfileState(profileState);
119
- profileStateRef.current = profileState;
120
- experience_jsShared.logger.debug('Profile State Changed', profileState);
121
131
  }
132
+ profileStateRef.current = changedProfileState;
133
+ experience_jsShared.logger.debug('Profile State Changed', changedProfileState);
134
+ setStrippedProfileState(formatProfileForHook(changedProfileState));
122
135
  });
136
+ // Clean up subscription when component unmounts
137
+ return () => {
138
+ if (typeof unsubscribe === 'function') {
139
+ unsubscribe();
140
+ experience_jsShared.logger.debug('Unsubscribed from profile state changes');
141
+ }
142
+ };
123
143
  }, []);
124
- const profileStateWithoutExperiences = __rest(profileState, ["experiences"]);
125
- return Object.assign(Object.assign({}, profileStateWithoutExperiences), {
126
- loading: profileState.status === 'loading'
127
- });
144
+ return strippedProfileState;
128
145
  };
129
146
 
130
147
  const usePersonalize = (baseline, variants, options = {
@@ -253,8 +270,8 @@ const MergeTag = ({
253
270
  fallback
254
271
  }) => {
255
272
  const {
256
- loading,
257
- profile
273
+ profile,
274
+ loading
258
275
  } = useProfile();
259
276
  if (loading || !profile) {
260
277
  return null;
package/index.esm.js CHANGED
@@ -77,33 +77,52 @@ function _objectWithoutPropertiesLoose(source, excluded) {
77
77
  }
78
78
 
79
79
  const _excluded$3 = ["experiences"];
80
+ function formatProfileForHook(profile) {
81
+ const profileStateWithoutExperiences = _objectWithoutPropertiesLoose(profile, _excluded$3);
82
+ return Object.assign({}, profileStateWithoutExperiences, {
83
+ loading: profile.status === 'loading'
84
+ });
85
+ }
86
+
87
+ /**
88
+ * Custom hook that provides access to the Ninetailed profile state
89
+ * with the 'experiences' property removed to prevent unnecessary re-renders.
90
+ *
91
+ * This hook handles profile state changes efficiently by:
92
+ * 1. Only updating state when actual changes occur
93
+ * 2. Removing the large 'experiences' object from the state
94
+ * 3. Properly cleaning up subscriptions when components unmount
95
+ *
96
+ * @returns The profile state without the 'experiences' property
97
+ */
80
98
  const useProfile = () => {
81
99
  const ninetailed = useNinetailed();
82
- const [profileState, setProfileState] = useState(ninetailed.profileState);
83
- const profileStateRef = useRef({});
100
+ const [strippedProfileState, setStrippedProfileState] = useState(formatProfileForHook(ninetailed.profileState));
84
101
 
85
- /**
86
- * This effect compares the old and new profile state before updating it.
87
- * We use a ref to avoid an infinite loop which can happen when an empty profile state was updated with no changes.
88
- * This behaviour occurred as the validation handling on the error property was not set properly in the "CreateProfile" and "UpdateProfile" endpoint types.
89
- * Furthermore, it was also observed, that it "only" occurred when the preview plugin was used in parallel.
90
- */
102
+ // Reference to track the previous profile state for comparison
103
+ const profileStateRef = useRef(ninetailed.profileState);
91
104
  useEffect(() => {
92
- ninetailed.onProfileChange(profileState => {
93
- if (isEqual(profileState, profileStateRef.current)) {
94
- logger.debug('Profile State Did Not Change', profileState);
105
+ const unsubscribe = ninetailed.onProfileChange(changedProfileState => {
106
+ // Skip update if the profile hasn't actually changed
107
+ // Here we compare the entire profile including experiences and changes
108
+ if (isEqual(changedProfileState, profileStateRef.current)) {
109
+ logger.debug('Profile State Did Not Change', changedProfileState);
95
110
  return;
96
- } else {
97
- setProfileState(profileState);
98
- profileStateRef.current = profileState;
99
- logger.debug('Profile State Changed', profileState);
100
111
  }
112
+ profileStateRef.current = changedProfileState;
113
+ logger.debug('Profile State Changed', changedProfileState);
114
+ setStrippedProfileState(formatProfileForHook(changedProfileState));
101
115
  });
116
+
117
+ // Clean up subscription when component unmounts
118
+ return () => {
119
+ if (typeof unsubscribe === 'function') {
120
+ unsubscribe();
121
+ logger.debug('Unsubscribed from profile state changes');
122
+ }
123
+ };
102
124
  }, []);
103
- const profileStateWithoutExperiences = _objectWithoutPropertiesLoose(profileState, _excluded$3);
104
- return Object.assign({}, profileStateWithoutExperiences, {
105
- loading: profileState.status === 'loading'
106
- });
125
+ return strippedProfileState;
107
126
  };
108
127
 
109
128
  const usePersonalize = (baseline, variants, options = {
@@ -232,8 +251,8 @@ const MergeTag = ({
232
251
  fallback
233
252
  }) => {
234
253
  const {
235
- loading,
236
- profile
254
+ profile,
255
+ loading
237
256
  } = useProfile();
238
257
  if (loading || !profile) {
239
258
  return null;
package/package.json CHANGED
@@ -1,11 +1,11 @@
1
1
  {
2
2
  "name": "@ninetailed/experience.js-react",
3
- "version": "7.11.0",
3
+ "version": "7.11.1",
4
4
  "description": "Ninetailed SDK for React",
5
5
  "dependencies": {
6
- "@ninetailed/experience.js": "7.11.0",
7
- "@ninetailed/experience.js-shared": "7.11.0",
8
- "@ninetailed/experience.js-plugin-analytics": "7.11.0",
6
+ "@ninetailed/experience.js": "7.11.1",
7
+ "@ninetailed/experience.js-shared": "7.11.1",
8
+ "@ninetailed/experience.js-plugin-analytics": "7.11.1",
9
9
  "radash": "10.9.0",
10
10
  "react-is": "18.2.0"
11
11
  },
@@ -1,53 +1,17 @@
1
- export declare const useProfile: () => {
1
+ import { ProfileState } from '@ninetailed/experience.js';
2
+ type UseProfileHookResult = Omit<ProfileState, 'experiences'> & {
2
3
  loading: boolean;
3
- from: "api" | "hydrated";
4
- status: "loading";
5
- profile: null;
6
- error: null;
7
- } | {
8
- loading: boolean;
9
- from: "api" | "hydrated";
10
- status: "success";
11
- profile: {
12
- id: string;
13
- stableId: string;
14
- random: number;
15
- audiences: string[];
16
- traits: import("@ninetailed/experience.js-shared").Properties;
17
- location: {
18
- coordinates?: {
19
- latitude: number;
20
- longitude: number;
21
- } | undefined;
22
- city?: string | undefined;
23
- postalCode?: string | undefined;
24
- region?: string | undefined;
25
- regionCode?: string | undefined;
26
- country?: string | undefined;
27
- countryCode?: string | undefined;
28
- continent?: string | undefined;
29
- timezone?: string | undefined;
30
- };
31
- session: {
32
- id: string;
33
- isReturningVisitor: boolean;
34
- landingPage: {
35
- path: string;
36
- url: string;
37
- query: Record<string, string>;
38
- referrer: string;
39
- search: string;
40
- };
41
- count: number;
42
- activeSessionLength: number;
43
- averageSessionLength: number;
44
- };
45
- };
46
- error: null;
47
- } | {
48
- loading: boolean;
49
- from: "api" | "hydrated";
50
- status: "error";
51
- profile: null;
52
- error: Error;
53
4
  };
5
+ /**
6
+ * Custom hook that provides access to the Ninetailed profile state
7
+ * with the 'experiences' property removed to prevent unnecessary re-renders.
8
+ *
9
+ * This hook handles profile state changes efficiently by:
10
+ * 1. Only updating state when actual changes occur
11
+ * 2. Removing the large 'experiences' object from the state
12
+ * 3. Properly cleaning up subscriptions when components unmount
13
+ *
14
+ * @returns The profile state without the 'experiences' property
15
+ */
16
+ export declare const useProfile: () => UseProfileHookResult;
17
+ export {};