Skip to content

Commit 3e726f9

Browse files
Merge pull request #153 from splitio/hooks_export
Export new hooks
2 parents 9d2ba00 + ecd1b0c commit 3e726f9

33 files changed

Lines changed: 83 additions & 497 deletions

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,6 @@
44
/lib
55
/es
66
/umd
7+
/types
78
/coverage
89
.scannerwork

CHANGES.txt

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,12 @@
1-
1.10.0 (September XX, 2023)
1+
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).
27
- 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).
38
- 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).
4-
- Updated the `useTreatments` hook to optimize feature flag evaluation. It now uses the `useMemo` hook to memoize calls to the SDK's `getTreatmentsWithConfig` function. This avoids re-evaluating feature flags when the hook is called with the same parameters and the feature flag definitions have not changed.
9+
- Updated the `useTreatments` hook to optimize feature flag evaluations.
510
- Updated linter and other dependencies for vulnerability fixes.
611
- 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.
712
- 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).

README.md

Lines changed: 20 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -20,7 +20,7 @@ Below is a simple example that describes the instantiation and most basic usage
2020
import React from 'react';
2121

2222
// Import SDK functions
23-
import { SplitFactory, SplitTreatments } from '@splitsoftware/splitio-react';
23+
import { SplitFactory, useSplitTreatments } from '@splitsoftware/splitio-react';
2424

2525
// Define your config object
2626
const CONFIG = {
@@ -30,25 +30,27 @@ const CONFIG = {
3030
}
3131
};
3232

33-
function MyReactComponent() {
33+
function MyComponent() {
34+
// Evaluate feature flags with useSplitTreatments hook
35+
const { treatments: { FEATURE_FLAG_NAME }, isReady } = useSplitTreatments({ names: ['FEATURE_FLAG_NAME'] });
36+
37+
// Check SDK readiness using isReady prop
38+
if (!isReady) return <div>Loading SDK ...</div>;
39+
40+
if (FEATURE_FLAG_NAME.treatment === 'on') {
41+
// return JSX for on treatment
42+
} else if (FEATURE_FLAG_NAME.treatment === 'off') {
43+
// return JSX for off treatment
44+
} else {
45+
// return JSX for control treatment
46+
};
47+
}
48+
49+
function MyApp() {
3450
return (
35-
/* Use SplitFactory to instantiate the SDK and makes it available to nested components */
51+
// Use SplitFactory to instantiate the SDK and makes it available to nested components
3652
<SplitFactory config={CONFIG} >
37-
{/* Evaluate feature flags with SplitTreatments component */}
38-
<SplitTreatments names={['FEATURE_FLAG_NAME']} >
39-
{({ treatments: { FEATURE_FLAG_NAME }, isReady }) => {
40-
// Check SDK readiness using isReady prop
41-
if (!isReady)
42-
return <div>Loading SDK ...</div>;
43-
if (FEATURE_FLAG_NAME.treatment === 'on') {
44-
// return JSX for on treatment
45-
} else if (FEATURE_FLAG_NAME.treatment === 'off') {
46-
// return JSX for off treatment
47-
} else {
48-
// return JSX for control treatment
49-
}
50-
}}
51-
</SplitTreatments>
53+
<MyComponent />
5254
</SplitFactory>
5355
);
5456
}

package-lock.json

Lines changed: 2 additions & 2 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-react",
3-
"version": "1.9.0",
3+
"version": "1.9.1-rc.0",
44
"description": "A React library to easily integrate and use Split JS SDK",
55
"main": "lib/index.js",
66
"module": "es/index.js",

src/__tests__/SplitTreatments.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -136,8 +136,8 @@ describe('SplitTreatments', () => {
136136
}}
137137
</SplitFactory>
138138
);
139-
expect(logSpy).toBeCalledWith('[ERROR] split names must be a non-empty array.');
140-
expect(logSpy).toBeCalledWith('[ERROR] you passed an invalid split name, split name must be a non-empty string.');
139+
expect(logSpy).toBeCalledWith('[ERROR] feature flag names must be a non-empty array.');
140+
expect(logSpy).toBeCalledWith('[ERROR] you passed an invalid feature flag name, feature flag name must be a non-empty string.');
141141

142142
done();
143143
});

src/__tests__/index.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,6 +12,8 @@ import {
1212
useManager as exportedUseManager,
1313
useTrack as exportedUseTrack,
1414
useTreatments as exportedUseTreatments,
15+
useSplitClient as exportedUseSplitClient,
16+
useSplitTreatments as exportedUseSplitTreatments,
1517
// Checks that types are exported. Otherwise, the test would fail with a TS error.
1618
ISplitClientChildProps,
1719
ISplitClientProps,
@@ -35,6 +37,8 @@ import { useClient } from '../useClient';
3537
import { useManager } from '../useManager';
3638
import { useTrack } from '../useTrack';
3739
import { useTreatments } from '../useTreatments';
40+
import { useSplitClient } from '../useSplitClient';
41+
import { useSplitTreatments } from '../useSplitTreatments';
3842

3943
describe('index', () => {
4044

@@ -55,6 +59,8 @@ describe('index', () => {
5559
expect(exportedUseManager).toBe(useManager);
5660
expect(exportedUseTrack).toBe(useTrack);
5761
expect(exportedUseTreatments).toBe(useTreatments);
62+
expect(exportedUseSplitClient).toBe(useSplitClient);
63+
expect(exportedUseSplitTreatments).toBe(useSplitTreatments);
5864
});
5965

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

src/__tests__/useSplitTreatments.test.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -152,8 +152,8 @@ describe('useSplitTreatments', () => {
152152
return null;
153153
})
154154
);
155-
expect(logSpy).toBeCalledWith('[ERROR] split names must be a non-empty array.');
156-
expect(logSpy).toBeCalledWith('[ERROR] you passed an invalid split name, split name must be a non-empty string.');
155+
expect(logSpy).toBeCalledWith('[ERROR] feature flag names must be a non-empty array.');
156+
expect(logSpy).toBeCalledWith('[ERROR] you passed an invalid feature flag name, feature flag name must be a non-empty string.');
157157
});
158158

159159
test('useSplitTreatments must update on SDK events', async () => {

src/__tests__/withSplitTreatments.test.tsx

Lines changed: 18 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -12,38 +12,42 @@ import { sdkBrowser } from './testUtils/sdkConfigs';
1212
import { withSplitFactory } from '../withSplitFactory';
1313
import { withSplitClient } from '../withSplitClient';
1414
import { withSplitTreatments } from '../withSplitTreatments';
15-
import { ISplitTreatmentsChildProps } from '../types';
1615
import { getControlTreatmentsWithConfig } from '../constants';
1716

1817
describe('withSplitTreatments', () => {
1918

2019
it(`passes Split props and outer props to the child.
21-
In this test, the value of "props.treatments" is obteined by the function "getControlTreatmentsWithConfig",
22-
and not "client.getTreatmentsWithConfig" since the client is not ready.`, (done) => {
20+
In this test, the value of "props.treatments" is obtained by the function "getControlTreatmentsWithConfig",
21+
and not "client.getTreatmentsWithConfig" since the client is not ready.`, () => {
2322
const featureFlagNames = ['split1', 'split2'];
23+
2424
const Component = withSplitFactory(sdkBrowser)<{ outerProp1: string, outerProp2: number }>(
2525
({ outerProp1, outerProp2, factory }) => {
2626
const SubComponent = withSplitClient('user1')<{ outerProp1: string, outerProp2: number }>(
2727
withSplitTreatments(featureFlagNames)(
28-
(props: ISplitTreatmentsChildProps & { outerProp1: string, outerProp2: number }) => {
28+
(props) => {
2929
const clientMock = factory!.client('user1');
30-
expect(props.outerProp1).toBe('outerProp1');
31-
expect(props.outerProp2).toBe(2);
3230
expect((clientMock.getTreatmentsWithConfig as jest.Mock).mock.calls.length).toBe(0);
33-
expect(props.treatments).toEqual(getControlTreatmentsWithConfig(featureFlagNames));
34-
expect(props.isReady).toBe(false);
35-
expect(props.isReadyFromCache).toBe(false);
36-
expect(props.hasTimedout).toBe(false);
37-
expect(props.isTimedout).toBe(false);
38-
expect(props.isDestroyed).toBe(false);
39-
expect(props.lastUpdate).toBe(0);
40-
done();
31+
32+
expect(props).toStrictEqual({
33+
factory: factory, client: clientMock,
34+
outerProp1: 'outerProp1', outerProp2: 2,
35+
treatments: getControlTreatmentsWithConfig(featureFlagNames),
36+
isReady: false,
37+
isReadyFromCache: false,
38+
hasTimedout: false,
39+
isTimedout: false,
40+
isDestroyed: false,
41+
lastUpdate: 0
42+
});
43+
4144
return null;
4245
}
4346
)
4447
);
4548
return <SubComponent outerProp1={outerProp1} outerProp2={outerProp2} />;
4649
});
50+
4751
render(<Component outerProp1='outerProp1' outerProp2={2} />);
4852
});
4953

src/index.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ export { useClient } from './useClient';
1616
export { useTreatments } from './useTreatments';
1717
export { useTrack } from './useTrack';
1818
export { useManager } from './useManager';
19+
export { useSplitClient } from './useSplitClient';
20+
export { useSplitTreatments } from './useSplitTreatments';
1921

2022
// SplitContext
2123
export { SplitContext } from './SplitContext';

0 commit comments

Comments
 (0)