Skip to content

Commit f75708f

Browse files
Added useTreatment, useTreatments, useTreatmentWithConfig and useTreatmentsWithConfig hooks to replace the now deprecated useSplitTreatments hook
1 parent aa039d6 commit f75708f

File tree

12 files changed

+336
-54
lines changed

12 files changed

+336
-54
lines changed

CHANGES.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
2.6.0 (October XX, 2025)
2+
- Added `useTreatment`, `useTreatments`, `useTreatmentWithConfig` and `useTreatmentsWithConfig` hooks to replace the now deprecated `useSplitTreatments` hook.
23
- Updated @splitsoftware/splitio package to version 11.7.1 that includes minor updates:
34
- Added support for custom loggers: added `logger` configuration option and `factory.Logger.setLogger` method to allow the SDK to use a custom logger.
45

5-
66
2.5.0 (September 18, 2025)
77
- Updated @splitsoftware/splitio package to version 11.6.0 that includes minor updates:
88
- Added `storage.wrapper` configuration option to allow the SDK to use a custom storage wrapper for the storage type `LOCALSTORAGE`. Default value is `window.localStorage`.

src/__tests__/utils.test.ts

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,24 @@
1-
import { CONTROL_WITH_CONFIG } from '../constants';
2-
import { getControlTreatmentsWithConfig } from '../utils';
1+
import { CONTROL, CONTROL_WITH_CONFIG } from '../constants';
2+
import { getControlTreatments } from '../utils';
33

4-
describe('getControlTreatmentsWithConfig', () => {
4+
describe('getControlTreatments', () => {
55

66
it('should return an empty object if an empty array is provided', () => {
7-
expect(Object.values(getControlTreatmentsWithConfig([])).length).toBe(0);
7+
expect(Object.values(getControlTreatments([], true)).length).toBe(0);
8+
expect(Object.values(getControlTreatments([], false)).length).toBe(0);
89
});
910

10-
it('should return an empty object if an empty array is provided', () => {
11+
it('should return an object with control treatments if an array of feature flag names is provided', () => {
1112
const featureFlagNames = ['split1', 'split2'];
12-
const treatments: SplitIO.TreatmentsWithConfig = getControlTreatmentsWithConfig(featureFlagNames);
13+
const treatmentsWithConfig: SplitIO.TreatmentsWithConfig = getControlTreatments(featureFlagNames, true);
14+
featureFlagNames.forEach((featureFlagName) => {
15+
expect(treatmentsWithConfig[featureFlagName]).toBe(CONTROL_WITH_CONFIG);
16+
});
17+
expect(Object.keys(treatmentsWithConfig).length).toBe(featureFlagNames.length);
18+
19+
const treatments: SplitIO.Treatments = getControlTreatments(featureFlagNames, false);
1320
featureFlagNames.forEach((featureFlagName) => {
14-
expect(treatments[featureFlagName]).toBe(CONTROL_WITH_CONFIG);
21+
expect(treatments[featureFlagName]).toBe(CONTROL);
1522
});
1623
expect(Object.keys(treatments).length).toBe(featureFlagNames.length);
1724
});

src/__tests__/withSplitTreatments.test.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -14,14 +14,14 @@ import { INITIAL_STATUS } from './testUtils/utils';
1414
import { withSplitFactory } from '../withSplitFactory';
1515
import { withSplitClient } from '../withSplitClient';
1616
import { withSplitTreatments } from '../withSplitTreatments';
17-
import { getControlTreatmentsWithConfig } from '../utils';
17+
import { getControlTreatments } from '../utils';
1818

1919
const featureFlagNames = ['split1', 'split2'];
2020

2121
describe('withSplitTreatments', () => {
2222

2323
it(`passes Split props and outer props to the child.
24-
In this test, the value of "props.treatments" is obtained by the function "getControlTreatmentsWithConfig",
24+
In this test, the value of "props.treatments" is obtained by the function "getControlTreatments",
2525
and not "client.getTreatmentsWithConfig" since the client is not ready.`, () => {
2626

2727
const Component = withSplitFactory(sdkBrowser)<{ outerProp1: string, outerProp2: number }>(
@@ -36,7 +36,7 @@ describe('withSplitTreatments', () => {
3636
...INITIAL_STATUS,
3737
factory: factory, client: clientMock,
3838
outerProp1: 'outerProp1', outerProp2: 2,
39-
treatments: getControlTreatmentsWithConfig(featureFlagNames),
39+
treatments: getControlTreatments(featureFlagNames, true),
4040
});
4141

4242
return null;

src/index.ts

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ export { SplitFactoryProvider } from './SplitFactoryProvider';
1313

1414
// Hooks
1515
export { useTrack } from './useTrack';
16+
export { useTreatment } from './useTreatment';
17+
export { useTreatments } from './useTreatments';
18+
export { useTreatmentWithConfig } from './useTreatmentWithConfig';
19+
export { useTreatmentsWithConfig } from './useTreatmentsWithConfig';
1620
export { useSplitClient } from './useSplitClient';
1721
export { useSplitTreatments } from './useSplitTreatments';
1822
export { useSplitManager } from './useSplitManager';
@@ -34,5 +38,11 @@ export type {
3438
IUpdateProps,
3539
IUseSplitClientOptions,
3640
IUseSplitTreatmentsOptions,
37-
IUseSplitManagerResult
41+
IUseSplitManagerResult,
42+
IUseTreatmentOptions,
43+
IUseTreatmentsOptions,
44+
IUseTreatmentResult,
45+
IUseTreatmentWithConfigResult,
46+
IUseTreatmentsResult,
47+
IUseTreatmentsWithConfigResult
3848
} from './types';

src/types.ts

Lines changed: 108 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -170,6 +170,9 @@ export interface ISplitClientProps extends IUseSplitClientOptions {
170170
children: ((props: ISplitClientChildProps) => ReactNode) | ReactNode;
171171
}
172172

173+
/**
174+
* Result of the `useSplitManager` hook.
175+
*/
173176
export interface IUseSplitManagerResult extends ISplitContextValues {
174177
/**
175178
* Split manager instance.
@@ -179,6 +182,17 @@ export interface IUseSplitManagerResult extends ISplitContextValues {
179182
manager?: SplitIO.IManager;
180183
}
181184

185+
type EvaluationOptions = SplitIO.EvaluationOptions & {
186+
187+
/**
188+
* An object of type Attributes used to evaluate the feature flags.
189+
*/
190+
attributes?: SplitIO.Attributes;
191+
}
192+
193+
/**
194+
* @deprecated `useSplitTreatments` will be removed in a future major release. We recommend replacing it with the `useTreatment*` hooks.
195+
*/
182196
export type GetTreatmentsOptions = ({
183197

184198
/**
@@ -193,27 +207,51 @@ export type GetTreatmentsOptions = ({
193207
*/
194208
flagSets: string[];
195209
names?: undefined;
196-
}) & {
210+
}) & EvaluationOptions;
197211

198-
/**
199-
* An object of type Attributes used to evaluate the feature flags.
200-
*/
201-
attributes?: SplitIO.Attributes;
212+
/**
213+
* Options object accepted by the `useSplitTreatments` hook, used to call `client.getTreatmentsWithConfig()`, or `client.getTreatmentsWithConfigByFlagSets()`,
214+
* depending on whether `names` or `flagSets` options are provided, and to retrieve the result along with the Split context.
215+
*
216+
* @deprecated `useSplitTreatments` will be removed in a future major release. We recommend replacing it with the `useTreatment*` hooks.
217+
*/
218+
export type IUseSplitTreatmentsOptions = GetTreatmentsOptions & IUseSplitClientOptions;
219+
220+
/**
221+
* Options object accepted by the `useTreatment` and `useTreatmentWithConfig` hooks.
222+
*/
223+
export type IUseTreatmentOptions = {
202224

203225
/**
204-
* Optional properties to append to the generated impression object sent to Split backend.
226+
* Feature flag name to evaluate.
205227
*/
206-
properties?: SplitIO.Properties;
207-
}
228+
name: string;
229+
} & EvaluationOptions & IUseSplitClientOptions;
230+
208231

209232
/**
210-
* Options object accepted by the `useSplitTreatments` hook, used to call `client.getTreatmentsWithConfig()`, or `client.getTreatmentsWithConfigByFlagSets()`,
211-
* depending on whether `names` or `flagSets` options are provided, and to retrieve the result along with the Split context.
233+
* Options object accepted by the `useTreatments` and `useTreatmentsWithConfig` hooks.
212234
*/
213-
export type IUseSplitTreatmentsOptions = GetTreatmentsOptions & IUseSplitClientOptions;
235+
export type IUseTreatmentsOptions = ({
236+
237+
/**
238+
* List of feature flag names to evaluate. Either this or the `flagSets` property must be provided. If both are provided, the `flagSets` option is ignored.
239+
*/
240+
names: string[];
241+
flagSets?: undefined;
242+
} | {
243+
244+
/**
245+
* List of feature flag sets to evaluate. Either this or the `names` property must be provided. If both are provided, the `flagSets` option is ignored.
246+
*/
247+
flagSets: string[];
248+
names?: undefined;
249+
}) & EvaluationOptions & IUseSplitClientOptions;
214250

215251
/**
216252
* SplitTreatments Child Props interface. These are the props that the child component receives from the 'SplitTreatments' component.
253+
*
254+
* @deprecated `SplitTreatments` will be removed in a future major release. We recommend replacing it with the `useTreatments*` hooks.
217255
*/
218256
export interface ISplitTreatmentsChildProps extends ISplitContextValues {
219257

@@ -231,9 +269,68 @@ export interface ISplitTreatmentsChildProps extends ISplitContextValues {
231269
treatments: SplitIO.TreatmentsWithConfig;
232270
}
233271

272+
/**
273+
* Result of the `useTreatment` hook.
274+
*/
275+
export interface IUseTreatmentResult extends ISplitContextValues {
276+
/**
277+
* The treatment string for a feature flag, returned by client.getTreatment().
278+
*/
279+
treatment: SplitIO.Treatment;
280+
}
281+
282+
/**
283+
* Result of the `useTreatmentWithConfig` hook.
284+
*/
285+
export interface IUseTreatmentWithConfigResult extends ISplitContextValues {
286+
/**
287+
* The treatment with config for a feature flag, returned by client.getTreatmentWithConfig().
288+
*/
289+
treatment: SplitIO.TreatmentWithConfig;
290+
}
291+
292+
/**
293+
* Result of the `useTreatments` hook.
294+
*/
295+
export interface IUseTreatmentsResult extends ISplitContextValues {
296+
/**
297+
* An object with the treatment strings for a bulk of feature flags, returned by client.getTreatments() or client.getTreatmentsByFlagSets().
298+
* For example:
299+
*
300+
* ```js
301+
* {
302+
* feature1: 'on',
303+
* feature2: 'off'
304+
* }
305+
* ```
306+
*/
307+
treatments: SplitIO.Treatments;
308+
}
309+
310+
/**
311+
* Result of the `useTreatmentsWithConfig` hook.
312+
*/
313+
export interface IUseTreatmentsWithConfigResult extends ISplitContextValues {
314+
315+
/**
316+
* An object with the treatments with configs for a bulk of feature flags, returned by client.getTreatmentsWithConfig() or client.getTreatmentsWithConfigByFlagSets().
317+
* Each existing configuration is a stringified version of the JSON you defined on the Split user interface. For example:
318+
*
319+
* ```js
320+
* {
321+
* feature1: { treatment: 'on', config: null },
322+
* feature2: { treatment: 'off', config: '{"bannerText":"Click here."}' }
323+
* }
324+
* ```
325+
*/
326+
treatments: SplitIO.TreatmentsWithConfig;
327+
}
328+
234329
/**
235330
* SplitTreatments Props interface. These are the props accepted by SplitTreatments component, used to call 'client.getTreatmentsWithConfig()', or 'client.getTreatmentsWithConfigByFlagSets()',
236331
* depending on whether `names` or `flagSets` props are provided, and to pass the result to the child component.
332+
*
333+
* @deprecated `SplitTreatments` will be removed in a future major release. We recommend replacing it with the `useTreatments*` hooks.
237334
*/
238335
export type ISplitTreatmentsProps = IUseSplitTreatmentsOptions & {
239336

src/useSplitTreatments.ts

Lines changed: 4 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
1-
import * as React from 'react';
2-
import { memoizeGetTreatmentsWithConfig } from './utils';
31
import { ISplitTreatmentsChildProps, IUseSplitTreatmentsOptions } from './types';
4-
import { useSplitClient } from './useSplitClient';
2+
import { useTreatmentsWithConfig } from '.';
53

64
/**
75
* `useSplitTreatments` is a hook that returns an Split Context object extended with a `treatments` property object that contains feature flag evaluations.
@@ -17,20 +15,9 @@ import { useSplitClient } from './useSplitClient';
1715
* ```
1816
*
1917
* @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/#get-treatments-with-configurations}
18+
*
19+
* @deprecated `useSplitTreatments` will be removed in a future major release. We recommend replacing it with the `useTreatment*` hooks.
2020
*/
2121
export function useSplitTreatments(options: IUseSplitTreatmentsOptions): ISplitTreatmentsChildProps {
22-
const context = useSplitClient({ ...options, attributes: undefined });
23-
const { client, lastUpdate } = context;
24-
const { names, flagSets, attributes, properties } = options;
25-
26-
const getTreatmentsWithConfig = React.useMemo(memoizeGetTreatmentsWithConfig, []);
27-
28-
// Shallow copy `client.getAttributes` result for memoization, as it returns the same reference unless `client.clearAttributes` is invoked.
29-
// Note: the same issue occurs with the `names` and `attributes` arguments if they are mutated directly by the user instead of providing a new object.
30-
const treatments = getTreatmentsWithConfig(client, lastUpdate, names, attributes, client ? { ...client.getAttributes() } : {}, flagSets, properties && { properties });
31-
32-
return {
33-
...context,
34-
treatments,
35-
};
22+
return useTreatmentsWithConfig(options);
3623
}

src/useTreatment.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react';
2+
import { memoizeGetTreatment } from './utils';
3+
import { IUseTreatmentResult, IUseTreatmentOptions } from './types';
4+
import { useSplitClient } from './useSplitClient';
5+
6+
/**
7+
* `useTreatment` is a hook that returns an Split Context object extended with a `treatment` property.
8+
* It uses the `useSplitClient` hook to access the client, and invokes the `client.getTreatment()` method.
9+
*
10+
* @param options - An options object with a feature flag name to evaluate, and an optional `attributes` and `splitKey` values to configure the client.
11+
* @returns A Split Context object extended with a Treatment instance, that might be a control treatment if the client is not available or ready, or if the provided feature flag name does not exist.
12+
*
13+
* @example
14+
* ```js
15+
* const { treatment, isReady, isReadyFromCache, hasTimedout, lastUpdate, ... } = useTreatment({ name: 'feature_1'});
16+
* ```
17+
*
18+
* @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/#multiple-evaluations-at-once}
19+
*/
20+
export function useTreatment(options: IUseTreatmentOptions): IUseTreatmentResult {
21+
const context = useSplitClient({ ...options, attributes: undefined });
22+
const { client, lastUpdate } = context;
23+
const { name, attributes, properties } = options;
24+
25+
const getTreatment = React.useMemo(memoizeGetTreatment, []);
26+
27+
// Shallow copy `client.getAttributes` result for memoization, as it returns the same reference unless `client.clearAttributes` is invoked.
28+
// Note: the same issue occurs with the `names` and `attributes` arguments if they are mutated directly by the user instead of providing a new object.
29+
const treatment = getTreatment(client, lastUpdate, [name], attributes, client ? { ...client.getAttributes() } : {}, undefined, properties && { properties });
30+
31+
return {
32+
...context,
33+
treatment,
34+
};
35+
}

src/useTreatmentWithConfig.ts

Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
import * as React from 'react';
2+
import { memoizeGetTreatmentWithConfig } from './utils';
3+
import { IUseTreatmentWithConfigResult, IUseTreatmentOptions } from './types';
4+
import { useSplitClient } from './useSplitClient';
5+
6+
/**
7+
* `useTreatmentWithConfig` is a hook that returns an Split Context object extended with a `treatment` property.
8+
* It uses the `useSplitClient` hook to access the client, and invokes the `client.getTreatmentWithConfig()` method.
9+
*
10+
* @param options - An options object with a feature flag name to evaluate, and an optional `attributes` and `splitKey` values to configure the client.
11+
* @returns A Split Context object extended with a TreatmentWithConfig instance, that might be a control treatment if the client is not available or ready, or if the provided feature flag name does not exist.
12+
*
13+
* @example
14+
* ```js
15+
* const { treatment: { treatment, config }, isReady, isReadyFromCache, hasTimedout, lastUpdate, ... } = useTreatmentWithConfig({ name: 'feature_1'});
16+
* ```
17+
*
18+
* @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/#get-treatments-with-configurations}
19+
*/
20+
export function useTreatmentWithConfig(options: IUseTreatmentOptions): IUseTreatmentWithConfigResult {
21+
const context = useSplitClient({ ...options, attributes: undefined });
22+
const { client, lastUpdate } = context;
23+
const { name, attributes, properties } = options;
24+
25+
const getTreatmentWithConfig = React.useMemo(memoizeGetTreatmentWithConfig, []);
26+
27+
// Shallow copy `client.getAttributes` result for memoization, as it returns the same reference unless `client.clearAttributes` is invoked.
28+
// Note: the same issue occurs with the `names` and `attributes` arguments if they are mutated directly by the user instead of providing a new object.
29+
const treatment = getTreatmentWithConfig(client, lastUpdate, [name], attributes, client ? { ...client.getAttributes() } : {}, undefined, properties && { properties });
30+
31+
return {
32+
...context,
33+
treatment,
34+
};
35+
}

src/useTreatments.ts

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import * as React from 'react';
2+
import { memoizeGetTreatments } from './utils';
3+
import { IUseTreatmentsResult, IUseTreatmentsOptions } from './types';
4+
import { useSplitClient } from './useSplitClient';
5+
6+
/**
7+
* `useTreatments` is a hook that returns an Split Context object extended with a `treatments` property object that contains feature flag evaluations.
8+
* It uses the `useSplitClient` hook to access the client, and invokes the `client.getTreatments()` method if the `names` option is provided,
9+
* or the `client.getTreatmentsByFlagSets()` method if the `flagSets` option is provided.
10+
*
11+
* @param options - An options object with a list of feature flag names or flag sets to evaluate, and an optional `attributes` and `splitKey` values to configure the client.
12+
* @returns A Split Context object extended with a Treatments instance, that might contain control treatments if the client is not available or ready, or if feature flag names do not exist.
13+
*
14+
* @example
15+
* ```js
16+
* const { treatments: { feature_1, feature_2 }, isReady, isReadyFromCache, hasTimedout, lastUpdate, ... } = useTreatments({ names: ['feature_1', 'feature_2']});
17+
* ```
18+
*
19+
* @see {@link https://developer.harness.io/docs/feature-management-experimentation/sdks-and-infrastructure/client-side-sdks/javascript-sdk/#multiple-evaluations-at-once}
20+
*/
21+
export function useTreatments(options: IUseTreatmentsOptions): IUseTreatmentsResult {
22+
const context = useSplitClient({ ...options, attributes: undefined });
23+
const { client, lastUpdate } = context;
24+
const { names, flagSets, attributes, properties } = options;
25+
26+
const getTreatments = React.useMemo(memoizeGetTreatments, []);
27+
28+
// Shallow copy `client.getAttributes` result for memoization, as it returns the same reference unless `client.clearAttributes` is invoked.
29+
// Note: the same issue occurs with the `names` and `attributes` arguments if they are mutated directly by the user instead of providing a new object.
30+
const treatments = getTreatments(client, lastUpdate, names, attributes, client ? { ...client.getAttributes() } : {}, flagSets, properties && { properties });
31+
32+
return {
33+
...context,
34+
treatments,
35+
};
36+
}

0 commit comments

Comments
 (0)