Skip to content

Commit 1424ab3

Browse files
Merge pull request #167 from splitio/hooks_useSplitManager
Add `useSplitManager` hook
2 parents 3e726f9 + c42fd8c commit 1424ab3

12 files changed

Lines changed: 141 additions & 50 deletions

CHANGES.txt

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,14 @@
11
1.10.0 (November XX, 2023)
2-
- Added new `useSplitClient` and `useSplitTreatments` hooks to use instead of `useClient` and `useTreatments` respectively, which are deprecated now.
3-
- The new hooks return the Split context object together with the SDK client and treatments respectively, rather than returning only the client and treatments as the deprecated hooks do. This way it is possible to access status properties, like `isReady`, from the hook's results, without having to use the `useContext` hook or the client `ready` promise.
4-
- They accept an options object as parameter, which support the same arguments than the deprecated hooks, plus new boolean options to control when the hook should re-render: `updateOnSdkReady`, `updateOnSdkReadyFromCache`, `updateOnSdkTimedout`, and `updateOnSdkUpdate`.
5-
- `useSplitTreatments` optimizes feature flag evaluations by using the `useMemo` hook to memoize calls to the SDK's `getTreatmentsWithConfig` method. This avoids re-evaluating feature flags when the hook is called with the same options and the feature flag definitions have not changed.
6-
- They fixed a bug in the deprecated hooks, which caused them to not re-render and re-evaluate feature flags when they consume a different SDK client than the context and its status updates (i.e., when it emits SDK_READY or other event).
7-
- Added TypeScript types and interfaces to the library index exports, allowing them to be imported from the library index. For example, `import type { ISplitFactoryProps } from '@splitsoftware/splitio-react';` (Related to issue https://github.com/splitio/react-client/issues/162).
2+
- Added new `useSplitClient`, `useSplitTreatments` and `useSplitManager` hooks as replacements for the now deprecated `useClient`, `useTreatments` and `useManager` hooks.
3+
- These new hooks return the Split context object along with the SDK client, treatments and manager respectively, enabling direct access to status properties like `isReady`, eliminating the need for using the `useContext` hook or the client's `ready` promise.
4+
- `useSplitClient` and `useSplitTreatments` accept an options object as parameter, which support the same arguments as their predecessors, with additional boolean options for controlling re-rendering: `updateOnSdkReady`, `updateOnSdkReadyFromCache`, `updateOnSdkTimedout`, and `updateOnSdkUpdate`.
5+
- `useSplitTreatments` optimizes feature flag evaluations by using the `useMemo` hook to memoize `getTreatmentsWithConfig` method calls from the SDK. This avoids re-evaluating feature flags when the hook is called with the same options and the feature flag definitions have not changed.
6+
- They fixed a bug in the deprecated `useClient` and `useTreatments` hooks, which caused them to not re-render and re-evaluate feature flags when they access a different SDK client than the context and its status updates (i.e., when it emits SDK_READY or other event).
7+
- Added TypeScript types and interfaces to the library index exports, allowing them to be imported, e.g., `import type { ISplitFactoryProps } from '@splitsoftware/splitio-react'` (Related to issue https://github.com/splitio/react-client/issues/162).
88
- Updated type declarations of the library components to not restrict the type of the `children` prop to ReactElement, allowing to pass any valid ReactNode value (Related to issue https://github.com/splitio/react-client/issues/164).
99
- Updated the `useTreatments` hook to optimize feature flag evaluations.
1010
- Updated linter and other dependencies for vulnerability fixes.
11-
- Bugfixing - To adhere to the rules of hooks and prevent React warnings, conditional code within hooks was removed. Previously, this code checked for the availability of the hooks API (available in React version 16.8.0 or above) and logged an error message. Now, using hooks with React versions below 16.8.0 will throw an error.
11+
- Bugfixing - Removed conditional code within hooks to adhere to the rules of hooks and prevent React warnings. Previously, this code checked for the availability of the hooks API (available in React version 16.8.0 or above) and logged an error message. Now, using hooks with React versions below 16.8.0 will throw an error.
1212
- Bugfixing - Updated `useClient` and `useTreatments` hooks to re-render and re-evaluate feature flags when they consume a different SDK client than the context and its status updates (i.e., when it emits SDK_READY or other event).
1313

1414
1.9.0 (July 18, 2023)

src/__tests__/index.test.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import {
1414
useTreatments as exportedUseTreatments,
1515
useSplitClient as exportedUseSplitClient,
1616
useSplitTreatments as exportedUseSplitTreatments,
17+
useSplitManager as exportedUseSplitManager,
1718
// Checks that types are exported. Otherwise, the test would fail with a TS error.
1819
ISplitClientChildProps,
1920
ISplitClientProps,
@@ -39,6 +40,7 @@ import { useTrack } from '../useTrack';
3940
import { useTreatments } from '../useTreatments';
4041
import { useSplitClient } from '../useSplitClient';
4142
import { useSplitTreatments } from '../useSplitTreatments';
43+
import { useSplitManager } from '../useSplitManager';
4244

4345
describe('index', () => {
4446

@@ -61,6 +63,7 @@ describe('index', () => {
6163
expect(exportedUseTreatments).toBe(useTreatments);
6264
expect(exportedUseSplitClient).toBe(useSplitClient);
6365
expect(exportedUseSplitTreatments).toBe(useSplitTreatments);
66+
expect(exportedUseSplitManager).toBe(useSplitManager);
6467
});
6568

6669
it('should export SplitContext', () => {

src/__tests__/useManager.test.tsx

Lines changed: 10 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -1,43 +1,21 @@
1-
import React from 'react';
2-
import { render } from '@testing-library/react';
3-
41
/** Mocks */
5-
import { mockSdk } from './testUtils/mockSplitSdk';
6-
jest.mock('@splitsoftware/splitio/client', () => {
7-
return { SplitFactory: mockSdk() };
8-
});
9-
import { SplitFactory as SplitSdk } from '@splitsoftware/splitio/client';
10-
import { sdkBrowser } from './testUtils/sdkConfigs';
2+
const useSplitManagerMock = jest.fn();
3+
jest.mock('../useSplitManager', () => ({
4+
useSplitManager: useSplitManagerMock
5+
}));
116

127
/** Test target */
13-
import { SplitFactory } from '../SplitFactory';
148
import { useManager } from '../useManager';
159

1610
describe('useManager', () => {
1711

18-
test('returns the factory manager from the Split context.', () => {
19-
const outerFactory = SplitSdk(sdkBrowser);
20-
let manager;
21-
render(
22-
<SplitFactory factory={outerFactory} >
23-
{React.createElement(() => {
24-
manager = useManager();
25-
return null;
26-
})}
27-
</SplitFactory>
28-
);
29-
expect(manager).toBe(outerFactory.manager());
30-
});
12+
test('calls useSplitManager with the correct arguments and returns the manager.', () => {
13+
const manager = 'manager';
14+
useSplitManagerMock.mockReturnValue({ manager, isReady: false });
15+
16+
expect(useManager()).toBe(manager);
3117

32-
test('returns null if invoked outside Split context.', () => {
33-
let manager;
34-
render(
35-
React.createElement(() => {
36-
manager = useManager();
37-
return null;
38-
})
39-
);
40-
expect(manager).toBe(null);
18+
expect(useSplitManagerMock).toHaveBeenCalledTimes(1);
4119
});
4220

4321
});
Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,80 @@
1+
import React from 'react';
2+
import { act, render } from '@testing-library/react';
3+
4+
/** Mocks */
5+
import { Event, mockSdk } from './testUtils/mockSplitSdk';
6+
jest.mock('@splitsoftware/splitio/client', () => {
7+
return { SplitFactory: mockSdk() };
8+
});
9+
import { SplitFactory as SplitSdk } from '@splitsoftware/splitio/client';
10+
import { sdkBrowser } from './testUtils/sdkConfigs';
11+
import { getStatus } from '../utils';
12+
13+
/** Test target */
14+
import { SplitFactory } from '../SplitFactory';
15+
import { useSplitManager } from '../useSplitManager';
16+
17+
describe('useSplitManager', () => {
18+
19+
test('returns the factory manager from the Split context, and updates when the context changes.', () => {
20+
const outerFactory = SplitSdk(sdkBrowser);
21+
let hookResult;
22+
render(
23+
<SplitFactory factory={outerFactory} >
24+
{React.createElement(() => {
25+
hookResult = useSplitManager();
26+
return null;
27+
})}
28+
</SplitFactory>
29+
);
30+
31+
expect(hookResult).toStrictEqual({
32+
manager: outerFactory.manager(),
33+
client: outerFactory.client(),
34+
factory: outerFactory,
35+
hasTimedout: false,
36+
isDestroyed: false,
37+
isReady: false,
38+
isReadyFromCache: false,
39+
isTimedout: false,
40+
lastUpdate: 0,
41+
});
42+
43+
act(() => (outerFactory.client() as any).__emitter__.emit(Event.SDK_READY));
44+
45+
expect(hookResult).toStrictEqual({
46+
manager: outerFactory.manager(),
47+
client: outerFactory.client(),
48+
factory: outerFactory,
49+
hasTimedout: false,
50+
isDestroyed: false,
51+
isReady: true,
52+
isReadyFromCache: false,
53+
isTimedout: false,
54+
lastUpdate: getStatus(outerFactory.client()).lastUpdate,
55+
});
56+
});
57+
58+
test('returns null if invoked outside Split context.', () => {
59+
let hookResult;
60+
render(
61+
React.createElement(() => {
62+
hookResult = useSplitManager();
63+
return null;
64+
})
65+
);
66+
67+
expect(hookResult).toStrictEqual({
68+
manager: null,
69+
client: null,
70+
factory: null,
71+
hasTimedout: false,
72+
isDestroyed: false,
73+
isReady: false,
74+
isReadyFromCache: false,
75+
isTimedout: false,
76+
lastUpdate: 0,
77+
});
78+
});
79+
80+
});

src/index.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -11,13 +11,14 @@ export { SplitTreatments } from './SplitTreatments';
1111
export { SplitClient } from './SplitClient';
1212
export { SplitFactory } from './SplitFactory';
1313

14-
// helper functions/hooks
14+
// Hooks
1515
export { useClient } from './useClient';
1616
export { useTreatments } from './useTreatments';
1717
export { useTrack } from './useTrack';
1818
export { useManager } from './useManager';
1919
export { useSplitClient } from './useSplitClient';
2020
export { useSplitTreatments } from './useSplitTreatments';
21+
export { useSplitManager } from './useSplitManager';
2122

2223
// SplitContext
2324
export { SplitContext } from './SplitContext';

src/useClient.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@ import { useSplitClient } from './useSplitClient';
55
* It uses the 'useContext' hook to access the context, which is updated by
66
* SplitFactory and SplitClient components in the hierarchy of components.
77
*
8-
* @return A Split Client instance, or null if used outside the scope of SplitFactory
8+
* @returns A Split Client instance, or null if used outside the scope of SplitFactory
9+
*
910
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#advanced-instantiate-multiple-sdk-clients}
1011
*
1112
* @deprecated Replace with the new `useSplitClient` hook.

src/useManager.ts

Lines changed: 6 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
1-
import React from 'react';
2-
import { SplitContext } from './SplitContext';
1+
import { useSplitManager } from './useSplitManager';
32

43
/**
54
* 'useManager' is a hook that returns the Manager instance from the Split factory.
65
* It uses the 'useContext' hook to access the factory at Split context, which is updated by
76
* the SplitFactory component.
87
*
9-
* @return A Split Manager instance, or null if used outside the scope of SplitFactory
8+
* @returns A Split Manager instance, or null if used outside the scope of SplitFactory
9+
*
1010
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#manager}
11+
*
12+
* @deprecated Replace with the new `useSplitManager` hook.
1113
*/
1214
export function useManager(): SplitIO.IManager | null {
13-
const { factory } = React.useContext(SplitContext);
14-
return factory ? factory.manager() : null;
15+
return useSplitManager().manager;
1516
}

src/useSplitClient.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ export const DEFAULT_UPDATE_OPTIONS = {
1414
* 'useSplitClient' is a hook that returns an Split Context object with the client and its status corresponding to the provided key and trafficType.
1515
* It uses the 'useContext' hook to access the context, which is updated by SplitFactory and SplitClient components in the hierarchy of components.
1616
*
17-
* @return A Split Context object
17+
* @returns A Split Context object
1818
*
1919
* @example
2020
* ```js

src/useSplitManager.ts

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
import React from 'react';
2+
import { SplitContext } from './SplitContext';
3+
import { ISplitContextValues } from './types';
4+
5+
/**
6+
* 'useSplitManager' is a hook that returns an Split Context object with the Manager instance from the Split factory.
7+
* It uses the 'useContext' hook to access the factory at Split context, which is updated by the SplitFactory component.
8+
*
9+
* @returns An object containing the Split context and the Split Manager instance, which is null if used outside the scope of SplitFactory
10+
*
11+
* @example
12+
* ```js
13+
* const { manager, isReady } = useSplitManager();
14+
* ```
15+
*
16+
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#manager}
17+
*/
18+
export function useSplitManager(): ISplitContextValues & { manager: SplitIO.IManager | null } {
19+
// Update options are not supported, because updates can be controlled at the SplitFactory component.
20+
const context = React.useContext(SplitContext);
21+
return {
22+
...context,
23+
manager: context.factory ? context.factory.manager() : null
24+
};
25+
}

src/useSplitTreatments.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,7 @@ import { useSplitClient } from './useSplitClient';
88
* 'useSplitTreatments' is a hook that returns an SplitContext object extended with a `treatments` property object that contains feature flag evaluations.
99
* It uses the 'useSplitClient' hook to access the client from the Split context, and invokes the 'getTreatmentsWithConfig' method.
1010
*
11-
* @return A Split Context object extended with a TreatmentsWithConfig instance, that might contain control treatments if the client is not available or ready, or if feature flag names do not exist.
11+
* @returns A Split Context object extended with a TreatmentsWithConfig instance, that might contain control treatments if the client is not available or ready, or if feature flag names do not exist.
1212
*
1313
* @example
1414
* ```js

0 commit comments

Comments
 (0)