Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
14 changes: 7 additions & 7 deletions CHANGES.txt
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
1.10.0 (November XX, 2023)
- Added new `useSplitClient` and `useSplitTreatments` hooks to use instead of `useClient` and `useTreatments` respectively, which are deprecated now.
- 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.
- 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`.
- `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.
- 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).
- 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).
- Added new `useSplitClient`, `useSplitTreatments` and `useSplitManager` hooks as replacements for the now deprecated `useClient`, `useTreatments` and `useManager` hooks.
- 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.
- `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`.
- `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.
- 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).
- 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).
- 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).
- Updated the `useTreatments` hook to optimize feature flag evaluations.
- Updated linter and other dependencies for vulnerability fixes.
- 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.
- 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.
- 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).

1.9.0 (July 18, 2023)
Expand Down
3 changes: 3 additions & 0 deletions src/__tests__/index.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import {
useTreatments as exportedUseTreatments,
useSplitClient as exportedUseSplitClient,
useSplitTreatments as exportedUseSplitTreatments,
useSplitManager as exportedUseSplitManager,
// Checks that types are exported. Otherwise, the test would fail with a TS error.
ISplitClientChildProps,
ISplitClientProps,
Expand All @@ -39,6 +40,7 @@ import { useTrack } from '../useTrack';
import { useTreatments } from '../useTreatments';
import { useSplitClient } from '../useSplitClient';
import { useSplitTreatments } from '../useSplitTreatments';
import { useSplitManager } from '../useSplitManager';

describe('index', () => {

Expand All @@ -61,6 +63,7 @@ describe('index', () => {
expect(exportedUseTreatments).toBe(useTreatments);
expect(exportedUseSplitClient).toBe(useSplitClient);
expect(exportedUseSplitTreatments).toBe(useSplitTreatments);
expect(exportedUseSplitManager).toBe(useSplitManager);
});

it('should export SplitContext', () => {
Expand Down
42 changes: 10 additions & 32 deletions src/__tests__/useManager.test.tsx
Original file line number Diff line number Diff line change
@@ -1,43 +1,21 @@
import React from 'react';
import { render } from '@testing-library/react';

/** Mocks */
import { mockSdk } from './testUtils/mockSplitSdk';
jest.mock('@splitsoftware/splitio/client', () => {
return { SplitFactory: mockSdk() };
});
import { SplitFactory as SplitSdk } from '@splitsoftware/splitio/client';
import { sdkBrowser } from './testUtils/sdkConfigs';
const useSplitManagerMock = jest.fn();
jest.mock('../useSplitManager', () => ({
useSplitManager: useSplitManagerMock
}));

/** Test target */
import { SplitFactory } from '../SplitFactory';
import { useManager } from '../useManager';

describe('useManager', () => {

test('returns the factory manager from the Split context.', () => {
const outerFactory = SplitSdk(sdkBrowser);
let manager;
render(
<SplitFactory factory={outerFactory} >
{React.createElement(() => {
manager = useManager();
return null;
})}
</SplitFactory>
);
expect(manager).toBe(outerFactory.manager());
});
test('calls useSplitManager with the correct arguments and returns the manager.', () => {
const manager = 'manager';
useSplitManagerMock.mockReturnValue({ manager, isReady: false });

expect(useManager()).toBe(manager);

test('returns null if invoked outside Split context.', () => {
let manager;
render(
React.createElement(() => {
manager = useManager();
return null;
})
);
expect(manager).toBe(null);
expect(useSplitManagerMock).toHaveBeenCalledTimes(1);
});

});
80 changes: 80 additions & 0 deletions src/__tests__/useSplitManager.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
import React from 'react';
import { act, render } from '@testing-library/react';

/** Mocks */
import { Event, mockSdk } from './testUtils/mockSplitSdk';
jest.mock('@splitsoftware/splitio/client', () => {
return { SplitFactory: mockSdk() };
});
import { SplitFactory as SplitSdk } from '@splitsoftware/splitio/client';
import { sdkBrowser } from './testUtils/sdkConfigs';
import { getStatus } from '../utils';

/** Test target */
import { SplitFactory } from '../SplitFactory';
import { useSplitManager } from '../useSplitManager';

describe('useSplitManager', () => {

test('returns the factory manager from the Split context, and updates when the context changes.', () => {
const outerFactory = SplitSdk(sdkBrowser);
let hookResult;
render(
<SplitFactory factory={outerFactory} >
{React.createElement(() => {
hookResult = useSplitManager();
return null;
})}
</SplitFactory>
);

expect(hookResult).toStrictEqual({
manager: outerFactory.manager(),
client: outerFactory.client(),
factory: outerFactory,
hasTimedout: false,
isDestroyed: false,
isReady: false,
isReadyFromCache: false,
isTimedout: false,
lastUpdate: 0,
});

act(() => (outerFactory.client() as any).__emitter__.emit(Event.SDK_READY));

expect(hookResult).toStrictEqual({
manager: outerFactory.manager(),
client: outerFactory.client(),
factory: outerFactory,
hasTimedout: false,
isDestroyed: false,
isReady: true,
isReadyFromCache: false,
isTimedout: false,
lastUpdate: getStatus(outerFactory.client()).lastUpdate,
});
});

test('returns null if invoked outside Split context.', () => {
let hookResult;
render(
React.createElement(() => {
hookResult = useSplitManager();
return null;
})
);

expect(hookResult).toStrictEqual({
manager: null,
client: null,
factory: null,
hasTimedout: false,
isDestroyed: false,
isReady: false,
isReadyFromCache: false,
isTimedout: false,
lastUpdate: 0,
});
});

});
3 changes: 2 additions & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,13 +11,14 @@ export { SplitTreatments } from './SplitTreatments';
export { SplitClient } from './SplitClient';
export { SplitFactory } from './SplitFactory';

// helper functions/hooks
// Hooks
export { useClient } from './useClient';
export { useTreatments } from './useTreatments';
export { useTrack } from './useTrack';
export { useManager } from './useManager';
export { useSplitClient } from './useSplitClient';
export { useSplitTreatments } from './useSplitTreatments';
export { useSplitManager } from './useSplitManager';

// SplitContext
export { SplitContext } from './SplitContext';
Expand Down
3 changes: 2 additions & 1 deletion src/useClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { useSplitClient } from './useSplitClient';
* It uses the 'useContext' hook to access the context, which is updated by
* SplitFactory and SplitClient components in the hierarchy of components.
*
* @return A Split Client instance, or null if used outside the scope of SplitFactory
* @returns A Split Client instance, or null if used outside the scope of SplitFactory
*
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#advanced-instantiate-multiple-sdk-clients}
*
* @deprecated Replace with the new `useSplitClient` hook.
Expand Down
11 changes: 6 additions & 5 deletions src/useManager.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
import React from 'react';
import { SplitContext } from './SplitContext';
import { useSplitManager } from './useSplitManager';

/**
* 'useManager' is a hook that returns the Manager instance from the Split factory.
* It uses the 'useContext' hook to access the factory at Split context, which is updated by
* the SplitFactory component.
*
* @return A Split Manager instance, or null if used outside the scope of SplitFactory
* @returns A Split Manager instance, or null if used outside the scope of SplitFactory
*
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#manager}
*
* @deprecated Replace with the new `useSplitManager` hook.
*/
export function useManager(): SplitIO.IManager | null {
const { factory } = React.useContext(SplitContext);
return factory ? factory.manager() : null;
return useSplitManager().manager;
}
2 changes: 1 addition & 1 deletion src/useSplitClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ export const DEFAULT_UPDATE_OPTIONS = {
* 'useSplitClient' is a hook that returns an Split Context object with the client and its status corresponding to the provided key and trafficType.
* It uses the 'useContext' hook to access the context, which is updated by SplitFactory and SplitClient components in the hierarchy of components.
*
* @return A Split Context object
* @returns A Split Context object
*
* @example
* ```js
Expand Down
25 changes: 25 additions & 0 deletions src/useSplitManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
import React from 'react';
import { SplitContext } from './SplitContext';
import { ISplitContextValues } from './types';

/**
* 'useSplitManager' is a hook that returns an Split Context object with the Manager instance from the Split factory.
* It uses the 'useContext' hook to access the factory at Split context, which is updated by the SplitFactory component.
*
* @returns An object containing the Split context and the Split Manager instance, which is null if used outside the scope of SplitFactory
*
* @example
* ```js
* const { manager, isReady } = useSplitManager();
* ```
*
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#manager}
*/
export function useSplitManager(): ISplitContextValues & { manager: SplitIO.IManager | null } {
// Update options are not supported, because updates can be controlled at the SplitFactory component.
const context = React.useContext(SplitContext);
return {
...context,
manager: context.factory ? context.factory.manager() : null
};
}
2 changes: 1 addition & 1 deletion src/useSplitTreatments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import { useSplitClient } from './useSplitClient';
* 'useSplitTreatments' is a hook that returns an SplitContext object extended with a `treatments` property object that contains feature flag evaluations.
* It uses the 'useSplitClient' hook to access the client from the Split context, and invokes the 'getTreatmentsWithConfig' method.
*
* @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.
* @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.
*
* @example
* ```js
Expand Down
3 changes: 2 additions & 1 deletion src/useTrack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,8 @@ const noOpFalse = () => false;
* 'useTrack' is a hook that returns the track method from a Split client.
* It uses the 'useContext' hook to access the client from the Split context.
*
* @return A track function bound to a Split client. If the client is not available, the result is a no-op function that returns false.
* @returns A track function bound to a Split client. If the client is not available, the result is a no-op function that returns false.
*
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#track}
*/
export function useTrack(splitKey?: SplitIO.SplitKey, trafficType?: string): SplitIO.IBrowserClient['track'] {
Expand Down
3 changes: 2 additions & 1 deletion src/useTreatments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import { useSplitTreatments } from './useSplitTreatments';
* It uses the 'useContext' hook to access the client from the Split context,
* and invokes the 'getTreatmentsWithConfig' method.
*
* @return A TreatmentsWithConfig instance, that might contain control treatments if the client is not available or ready, or if feature flag names do not exist.
* @returns A TreatmentsWithConfig instance, that might contain control treatments if the client is not available or ready, or if feature flag names do not exist.
*
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#get-treatments-with-configurations}
*
* @deprecated Replace with the new `useSplitTreatments` hook.
Expand Down