@ninetailed/experience.js-react 7.11.1 → 7.12.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/index.cjs.js +78 -0
- package/index.esm.js +79 -2
- package/package.json +4 -4
- package/src/lib/index.d.ts +1 -0
- package/src/lib/useFlag.d.ts +22 -0
package/index.cjs.js
CHANGED
|
@@ -151,6 +151,83 @@ const usePersonalize = (baseline, variants, options = {
|
|
|
151
151
|
return experience_js.selectVariant(baseline, variants, profile, options);
|
|
152
152
|
};
|
|
153
153
|
|
|
154
|
+
/**
|
|
155
|
+
* Custom hook to retrieve a specific feature flag from Ninetailed changes.
|
|
156
|
+
*
|
|
157
|
+
* @param flagKey - The key of the feature flag to retrieve
|
|
158
|
+
* @param defaultValue - The default value to use if the flag is not found
|
|
159
|
+
* @returns An object containing the flag value and status information
|
|
160
|
+
*/
|
|
161
|
+
function useFlag(flagKey, defaultValue) {
|
|
162
|
+
const ninetailed = useNinetailed();
|
|
163
|
+
const lastProcessedState = React.useRef(null);
|
|
164
|
+
const [result, setResult] = React.useState({
|
|
165
|
+
value: defaultValue,
|
|
166
|
+
status: 'loading',
|
|
167
|
+
error: null
|
|
168
|
+
});
|
|
169
|
+
React.useEffect(() => {
|
|
170
|
+
// Reset state when dependencies change
|
|
171
|
+
setResult({
|
|
172
|
+
value: defaultValue,
|
|
173
|
+
status: 'loading',
|
|
174
|
+
error: null
|
|
175
|
+
});
|
|
176
|
+
lastProcessedState.current = null;
|
|
177
|
+
const unsubscribe = ninetailed.onChangesChange(changesState => {
|
|
178
|
+
if (lastProcessedState.current && radash.isEqual(lastProcessedState.current, changesState)) {
|
|
179
|
+
experience_jsShared.logger.debug('Change State Did Not Change', changesState);
|
|
180
|
+
return;
|
|
181
|
+
}
|
|
182
|
+
lastProcessedState.current = changesState;
|
|
183
|
+
if (changesState.status === 'loading') {
|
|
184
|
+
// Don't use a function updater here to avoid type issues
|
|
185
|
+
setResult({
|
|
186
|
+
value: defaultValue,
|
|
187
|
+
status: 'loading',
|
|
188
|
+
error: null
|
|
189
|
+
});
|
|
190
|
+
return;
|
|
191
|
+
}
|
|
192
|
+
if (changesState.status === 'error') {
|
|
193
|
+
setResult({
|
|
194
|
+
value: defaultValue,
|
|
195
|
+
status: 'error',
|
|
196
|
+
error: changesState.error
|
|
197
|
+
});
|
|
198
|
+
return;
|
|
199
|
+
}
|
|
200
|
+
try {
|
|
201
|
+
// Find the change with our flag key
|
|
202
|
+
const change = changesState.changes.find(change => change.key === flagKey);
|
|
203
|
+
if (change && change.type === experience_jsShared.ChangeTypes.Variable) {
|
|
204
|
+
const flagValue = change.value;
|
|
205
|
+
setResult({
|
|
206
|
+
value: flagValue,
|
|
207
|
+
status: 'success',
|
|
208
|
+
error: null
|
|
209
|
+
});
|
|
210
|
+
} else {
|
|
211
|
+
// Flag not found or wrong type, use default
|
|
212
|
+
setResult({
|
|
213
|
+
value: defaultValue,
|
|
214
|
+
status: 'success',
|
|
215
|
+
error: null
|
|
216
|
+
});
|
|
217
|
+
}
|
|
218
|
+
} catch (error) {
|
|
219
|
+
setResult({
|
|
220
|
+
value: defaultValue,
|
|
221
|
+
status: 'error',
|
|
222
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
223
|
+
});
|
|
224
|
+
}
|
|
225
|
+
});
|
|
226
|
+
return unsubscribe;
|
|
227
|
+
}, [ninetailed, flagKey, defaultValue]);
|
|
228
|
+
return result;
|
|
229
|
+
}
|
|
230
|
+
|
|
154
231
|
const TrackHasSeenComponent = ({
|
|
155
232
|
children,
|
|
156
233
|
variant,
|
|
@@ -615,6 +692,7 @@ exports.NinetailedProvider = NinetailedProvider;
|
|
|
615
692
|
exports.Personalize = Personalize;
|
|
616
693
|
exports.TrackHasSeenComponent = TrackHasSeenComponent;
|
|
617
694
|
exports.useExperience = useExperience;
|
|
695
|
+
exports.useFlag = useFlag;
|
|
618
696
|
exports.useNinetailed = useNinetailed;
|
|
619
697
|
exports.usePersonalize = usePersonalize;
|
|
620
698
|
exports.useProfile = useProfile;
|
package/index.esm.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import React, { createContext, useMemo, useContext, useState, useRef, useEffect, createElement, forwardRef } from 'react';
|
|
2
2
|
import { Ninetailed, selectVariant, selectHasExperienceVariants } from '@ninetailed/experience.js';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
|
-
import { logger, isBrowser } from '@ninetailed/experience.js-shared';
|
|
4
|
+
import { logger, ChangeTypes, isBrowser } from '@ninetailed/experience.js-shared';
|
|
5
5
|
import { isEqual, get } from 'radash';
|
|
6
6
|
import { isForwardRef } from 'react-is';
|
|
7
7
|
|
|
@@ -132,6 +132,83 @@ const usePersonalize = (baseline, variants, options = {
|
|
|
132
132
|
return selectVariant(baseline, variants, profile, options);
|
|
133
133
|
};
|
|
134
134
|
|
|
135
|
+
/**
|
|
136
|
+
* Custom hook to retrieve a specific feature flag from Ninetailed changes.
|
|
137
|
+
*
|
|
138
|
+
* @param flagKey - The key of the feature flag to retrieve
|
|
139
|
+
* @param defaultValue - The default value to use if the flag is not found
|
|
140
|
+
* @returns An object containing the flag value and status information
|
|
141
|
+
*/
|
|
142
|
+
function useFlag(flagKey, defaultValue) {
|
|
143
|
+
const ninetailed = useNinetailed();
|
|
144
|
+
const lastProcessedState = useRef(null);
|
|
145
|
+
const [result, setResult] = useState({
|
|
146
|
+
value: defaultValue,
|
|
147
|
+
status: 'loading',
|
|
148
|
+
error: null
|
|
149
|
+
});
|
|
150
|
+
useEffect(() => {
|
|
151
|
+
// Reset state when dependencies change
|
|
152
|
+
setResult({
|
|
153
|
+
value: defaultValue,
|
|
154
|
+
status: 'loading',
|
|
155
|
+
error: null
|
|
156
|
+
});
|
|
157
|
+
lastProcessedState.current = null;
|
|
158
|
+
const unsubscribe = ninetailed.onChangesChange(changesState => {
|
|
159
|
+
if (lastProcessedState.current && isEqual(lastProcessedState.current, changesState)) {
|
|
160
|
+
logger.debug('Change State Did Not Change', changesState);
|
|
161
|
+
return;
|
|
162
|
+
}
|
|
163
|
+
lastProcessedState.current = changesState;
|
|
164
|
+
if (changesState.status === 'loading') {
|
|
165
|
+
// Don't use a function updater here to avoid type issues
|
|
166
|
+
setResult({
|
|
167
|
+
value: defaultValue,
|
|
168
|
+
status: 'loading',
|
|
169
|
+
error: null
|
|
170
|
+
});
|
|
171
|
+
return;
|
|
172
|
+
}
|
|
173
|
+
if (changesState.status === 'error') {
|
|
174
|
+
setResult({
|
|
175
|
+
value: defaultValue,
|
|
176
|
+
status: 'error',
|
|
177
|
+
error: changesState.error
|
|
178
|
+
});
|
|
179
|
+
return;
|
|
180
|
+
}
|
|
181
|
+
try {
|
|
182
|
+
// Find the change with our flag key
|
|
183
|
+
const change = changesState.changes.find(change => change.key === flagKey);
|
|
184
|
+
if (change && change.type === ChangeTypes.Variable) {
|
|
185
|
+
const flagValue = change.value;
|
|
186
|
+
setResult({
|
|
187
|
+
value: flagValue,
|
|
188
|
+
status: 'success',
|
|
189
|
+
error: null
|
|
190
|
+
});
|
|
191
|
+
} else {
|
|
192
|
+
// Flag not found or wrong type, use default
|
|
193
|
+
setResult({
|
|
194
|
+
value: defaultValue,
|
|
195
|
+
status: 'success',
|
|
196
|
+
error: null
|
|
197
|
+
});
|
|
198
|
+
}
|
|
199
|
+
} catch (error) {
|
|
200
|
+
setResult({
|
|
201
|
+
value: defaultValue,
|
|
202
|
+
status: 'error',
|
|
203
|
+
error: error instanceof Error ? error : new Error(String(error))
|
|
204
|
+
});
|
|
205
|
+
}
|
|
206
|
+
});
|
|
207
|
+
return unsubscribe;
|
|
208
|
+
}, [ninetailed, flagKey, defaultValue]);
|
|
209
|
+
return result;
|
|
210
|
+
}
|
|
211
|
+
|
|
135
212
|
const TrackHasSeenComponent = ({
|
|
136
213
|
children,
|
|
137
214
|
variant,
|
|
@@ -590,4 +667,4 @@ const ESRLoadingComponent = _ref => {
|
|
|
590
667
|
}));
|
|
591
668
|
};
|
|
592
669
|
|
|
593
|
-
export { DefaultExperienceLoadingComponent, ESRLoadingComponent, ESRProvider, Experience, MergeTag, NinetailedProvider, Personalize, TrackHasSeenComponent, useExperience, useNinetailed, usePersonalize, useProfile };
|
|
670
|
+
export { DefaultExperienceLoadingComponent, ESRLoadingComponent, ESRProvider, Experience, MergeTag, NinetailedProvider, Personalize, TrackHasSeenComponent, useExperience, useFlag, useNinetailed, usePersonalize, useProfile };
|
package/package.json
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@ninetailed/experience.js-react",
|
|
3
|
-
"version": "7.
|
|
3
|
+
"version": "7.12.0",
|
|
4
4
|
"description": "Ninetailed SDK for React",
|
|
5
5
|
"dependencies": {
|
|
6
|
-
"@ninetailed/experience.js": "7.
|
|
7
|
-
"@ninetailed/experience.js-shared": "7.
|
|
8
|
-
"@ninetailed/experience.js-plugin-analytics": "7.
|
|
6
|
+
"@ninetailed/experience.js": "7.12.0",
|
|
7
|
+
"@ninetailed/experience.js-shared": "7.12.0",
|
|
8
|
+
"@ninetailed/experience.js-plugin-analytics": "7.12.0",
|
|
9
9
|
"radash": "10.9.0",
|
|
10
10
|
"react-is": "18.2.0"
|
|
11
11
|
},
|
package/src/lib/index.d.ts
CHANGED
|
@@ -5,6 +5,7 @@ export type { NinetailedProviderProps, NinetailedProviderInstantiationProps, } f
|
|
|
5
5
|
export { useNinetailed } from './useNinetailed';
|
|
6
6
|
export { useProfile } from './useProfile';
|
|
7
7
|
export { usePersonalize } from './usePersonalize';
|
|
8
|
+
export { useFlag } from './useFlag';
|
|
8
9
|
export { Personalize } from './Personalize';
|
|
9
10
|
export type { PersonalizedComponent } from './Personalize';
|
|
10
11
|
export { MergeTag } from './MergeTag';
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { AllowedVariableType } from '@ninetailed/experience.js-shared';
|
|
2
|
+
export type FlagResult<T> = {
|
|
3
|
+
status: 'loading';
|
|
4
|
+
value: T;
|
|
5
|
+
error: null;
|
|
6
|
+
} | {
|
|
7
|
+
status: 'success';
|
|
8
|
+
value: T;
|
|
9
|
+
error: null;
|
|
10
|
+
} | {
|
|
11
|
+
status: 'error';
|
|
12
|
+
value: T;
|
|
13
|
+
error: Error;
|
|
14
|
+
};
|
|
15
|
+
/**
|
|
16
|
+
* Custom hook to retrieve a specific feature flag from Ninetailed changes.
|
|
17
|
+
*
|
|
18
|
+
* @param flagKey - The key of the feature flag to retrieve
|
|
19
|
+
* @param defaultValue - The default value to use if the flag is not found
|
|
20
|
+
* @returns An object containing the flag value and status information
|
|
21
|
+
*/
|
|
22
|
+
export declare function useFlag<T extends AllowedVariableType>(flagKey: string, defaultValue: T): FlagResult<T>;
|