Skip to content

Commit 7eb452f

Browse files
Merge branch 'hooks_update_options' into hooks_polishing
2 parents 261c038 + f1cc018 commit 7eb452f

15 files changed

Lines changed: 486 additions & 524 deletions

.eslintrc.js

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ module.exports = {
2929
'eol-last': ['error', 'always'],
3030
'keyword-spacing': 'error',
3131
'no-trailing-spaces': 'error',
32-
'space-before-function-paren': ['error', {'named': 'never'}],
32+
'space-before-function-paren': ['error', { 'named': 'never' }],
3333
'react/display-name': 'off',
3434
'@typescript-eslint/no-empty-function': 'off',
3535
'@typescript-eslint/no-inferrable-types': 'off',
@@ -40,6 +40,11 @@ module.exports = {
4040
'destructuring': 'all'
4141
}]
4242
},
43+
'settings': {
44+
'react': {
45+
'version': '16.3.0' // minimum supported version of React
46+
}
47+
},
4348
'overrides': [{
4449
'files': ['src/**/*.ts', 'src/**/*.tsx'],
4550
'excludedFiles': ['src/**/__tests__/**'],
@@ -53,7 +58,7 @@ module.exports = {
5358
},
5459
'rules': {
5560
'no-restricted-syntax': ['error', 'ForOfStatement', 'ForInStatement'],
56-
'compat/compat': ['error', 'defaults, not ie < 11'],
61+
'compat/compat': ['error', 'defaults, ie 11'],
5762
'no-throw-literal': 'error',
5863
'import/no-self-import': 'error',
5964
'import/no-default-export': 'error',

CHANGES.txt

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
1-
1.10.0 (XXX XX, 2023)
2-
- Bugfixing - Removed check of hooks availability to follow rules of hooks. If attempting to use hooks with React below version 16.8.0, an error will be thrown rather than logging an error message.
1+
1.10.0 (September XX, 2023)
2+
- 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).
3+
- Updated linter and other dependencies for vulnerability fixes.
4+
- 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.
5+
- 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).
36

47
1.9.0 (July 18, 2023)
58
- Updated some transitive dependencies for vulnerability fixes.

package-lock.json

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

package.json

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -75,10 +75,10 @@
7575
"@types/react-dom": "^18.0.0",
7676
"@types/react-test-renderer": "^18.0.0",
7777
"@types/shallowequal": "^1.1.1",
78-
"@typescript-eslint/eslint-plugin": "^5.55.0",
79-
"@typescript-eslint/parser": "^5.55.0",
80-
"eslint": "^8.36.0",
81-
"eslint-plugin-compat": "^4.1.2",
78+
"@typescript-eslint/eslint-plugin": "^6.6.0",
79+
"@typescript-eslint/parser": "^6.6.0",
80+
"eslint": "^8.48.0",
81+
"eslint-plugin-compat": "^4.2.0",
8282
"eslint-plugin-import": "^2.27.5",
8383
"eslint-plugin-react": "^7.32.2",
8484
"eslint-plugin-react-hooks": "^4.6.0",

src/SplitClient.tsx

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { SplitContext } from './SplitContext';
33
import { ISplitClientProps, ISplitContextValues, IUpdateProps } from './types';
44
import { ERROR_SC_NO_FACTORY } from './constants';
55
import { getStatus, getSplitClient, initAttributes, IClientWithContext } from './utils';
6+
import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
67

78
/**
89
* Common component used to handle the status and events of a Split client passed as prop.
@@ -11,18 +12,15 @@ import { getStatus, getSplitClient, initAttributes, IClientWithContext } from '.
1112
export class SplitComponent extends React.Component<IUpdateProps & { factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null, attributes?: SplitIO.Attributes, children: any }, ISplitContextValues> {
1213

1314
static defaultProps = {
14-
updateOnSdkUpdate: false,
15-
updateOnSdkTimedout: false,
16-
updateOnSdkReady: true,
17-
updateOnSdkReadyFromCache: true,
1815
children: null,
1916
factory: null,
2017
client: null,
18+
...DEFAULT_UPDATE_OPTIONS,
2119
}
2220

2321
// Using `getDerivedStateFromProps` since the state depends on the status of the client in props, which might change over time.
2422
// It could be avoided by removing the client and its status from the component state.
25-
// But it implies to have another instance property to use instead of the state, because we need a unique reference value for SplitContext.Producer
23+
// But it implies to have another instance property to use instead of the state, because we need a unique reference value for SplitContext.Provider
2624
static getDerivedStateFromProps(props: ISplitClientProps & { factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null }, state: ISplitContextValues) {
2725
const { client, factory, attributes } = props;
2826
// initAttributes can be called in the `render` method too, but it is better here for separation of concerns

src/SplitFactory.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { SplitComponent } from './SplitClient';
44
import { ISplitFactoryProps } from './types';
55
import { WARN_SF_CONFIG_AND_FACTORY, ERROR_SF_NO_CONFIG_AND_FACTORY } from './constants';
66
import { getSplitFactory, destroySplitFactory, IFactoryWithClients, getSplitClient } from './utils';
7+
import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
78

89
/**
910
* SplitFactory will initialize the Split SDK and its main client, listen for its events in order to update the Split Context,
@@ -18,11 +19,8 @@ import { getSplitFactory, destroySplitFactory, IFactoryWithClients, getSplitClie
1819
export class SplitFactory extends React.Component<ISplitFactoryProps, { factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null }> {
1920

2021
static defaultProps: ISplitFactoryProps = {
21-
updateOnSdkUpdate: false,
22-
updateOnSdkTimedout: false,
23-
updateOnSdkReady: true,
24-
updateOnSdkReadyFromCache: true,
2522
children: null,
23+
...DEFAULT_UPDATE_OPTIONS,
2624
};
2725

2826
readonly state: Readonly<{ factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null }>;

src/__tests__/SplitClient.test.tsx

Lines changed: 12 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -253,36 +253,18 @@ describe('SplitClient', () => {
253253
this.state = { splitKey: 'user1' };
254254
}
255255

256-
componentDidMount() {
257-
setTimeout(() => {
258-
act(() => this.setState({ splitKey: 'user2' }));
259-
setTimeout(() => {
260-
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_READY_TIMED_OUT));
261-
setTimeout(() => {
262-
act(() => (outerFactory as any).client('user1').__emitter__.emit(Event.SDK_READY_TIMED_OUT));
263-
setTimeout(() => {
264-
act(() => this.setState({ splitKey: 'user3' }));
265-
setTimeout(() => {
266-
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_READY));
267-
setTimeout(() => {
268-
act(() => (outerFactory as any).client('user3').__emitter__.emit(Event.SDK_READY));
269-
setTimeout(() => {
270-
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_UPDATE));
271-
setTimeout(() => {
272-
act(() => (outerFactory as any).client('user3').__emitter__.emit(Event.SDK_UPDATE));
273-
setTimeout(() => {
274-
expect(renderTimes).toBe(6);
275-
276-
done();
277-
});
278-
});
279-
});
280-
});
281-
});
282-
});
283-
});
284-
});
285-
});
256+
async componentDidMount() {
257+
await act(() => this.setState({ splitKey: 'user2' }));
258+
await act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_READY_TIMED_OUT));
259+
await act(() => (outerFactory as any).client('user1').__emitter__.emit(Event.SDK_READY_TIMED_OUT));
260+
await act(() => this.setState({ splitKey: 'user3' }));
261+
await act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_READY));
262+
await act(() => (outerFactory as any).client('user3').__emitter__.emit(Event.SDK_READY));
263+
await act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_UPDATE));
264+
await act(() => (outerFactory as any).client('user3').__emitter__.emit(Event.SDK_UPDATE));
265+
expect(renderTimes).toBe(6);
266+
267+
done();
286268
}
287269

288270
render() {

src/__tests__/index.test.ts

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,4 @@
1+
/* eslint-disable @typescript-eslint/no-unused-vars */
12
import {
23
SplitContext as ExportedSplitContext,
34
SplitSdk as ExportedSplitSdk,
@@ -11,6 +12,16 @@ import {
1112
useManager as exportedUseManager,
1213
useTrack as exportedUseTrack,
1314
useTreatments as exportedUseTreatments,
15+
// Checks that types are exported. Otherwise, the test would fail with a TS error.
16+
ISplitClientChildProps,
17+
ISplitClientProps,
18+
ISplitContextValues,
19+
ISplitFactoryChildProps,
20+
ISplitFactoryProps,
21+
ISplitStatus,
22+
ISplitTreatmentsChildProps,
23+
ISplitTreatmentsProps,
24+
IUpdateProps
1425
} from '../index';
1526
import { SplitContext } from '../SplitContext';
1627
import { SplitFactory as SplitioEntrypoint } from '@splitsoftware/splitio/client';

src/__tests__/useSplitClient.test.tsx

Lines changed: 11 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -30,19 +30,25 @@ test('useSplitClient must update on SDK events', () => {
3030
<SplitContext.Consumer>
3131
{() => countSplitContext++}
3232
</SplitContext.Consumer>
33-
<SplitClient splitKey={sdkBrowser.core.key}>
33+
<SplitClient splitKey={sdkBrowser.core.key} trafficType={sdkBrowser.core.trafficType}
34+
/* Disabling update props is ineffective because the wrapping SplitFactory has them enabled: */
35+
updateOnSdkReady={false} updateOnSdkReadyFromCache={false}
36+
>
3437
{() => { countSplitClient++; return null }}
3538
</SplitClient>
36-
<SplitClient splitKey={'user_2'}>
37-
{() => { countSplitClientUser2++; return null }}
38-
</SplitClient>
3939
{React.createElement(() => {
40-
const { client } = useSplitClient(sdkBrowser.core.key, sdkBrowser.core.trafficType, { att1: 'att1' });
40+
// Equivalent to
41+
// - Using config key and traffic type: `const { client } = useSplitClient(sdkBrowser.core.key, sdkBrowser.core.trafficType, { att1: 'att1' });`
42+
// - Disabling update props, since the wrapping SplitFactory has them enabled: `const { client } = useSplitClient(undefined, undefined, { att1: 'att1' }, { updateOnSdkReady: false, updateOnSdkReadyFromCache: false });`
43+
const { client } = useSplitClient(undefined, undefined, { att1: 'att1' });
4144
expect(client).toBe(mainClient); // Assert that the main client was retrieved.
4245
expect(client!.getAttributes()).toEqual({ att1: 'att1' }); // Assert that the client was retrieved with the provided attributes.
4346
countUseSplitClient++;
4447
return null;
4548
})}
49+
<SplitClient splitKey={'user_2'}>
50+
{() => { countSplitClientUser2++; return null }}
51+
</SplitClient>
4652
{React.createElement(() => {
4753
const { client } = useSplitClient('user_2');
4854
expect(client).toBe(user2Client);

src/__tests__/useSplitTreatments.test.tsx

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,12 @@ test('useSplitTreatments must update on SDK events', async () => {
7575
</SplitFactory>
7676
);
7777

78-
await act(() => mainClient.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
79-
await act(() => mainClient.__emitter__.emit(Event.SDK_READY));
80-
await act(() => mainClient.__emitter__.emit(Event.SDK_UPDATE));
81-
await act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
82-
await act(() => user2Client.__emitter__.emit(Event.SDK_READY));
83-
await act(() => user2Client.__emitter__.emit(Event.SDK_UPDATE));
78+
act(() => mainClient.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
79+
act(() => mainClient.__emitter__.emit(Event.SDK_READY));
80+
act(() => mainClient.__emitter__.emit(Event.SDK_UPDATE));
81+
act(() => user2Client.__emitter__.emit(Event.SDK_READY_FROM_CACHE));
82+
act(() => user2Client.__emitter__.emit(Event.SDK_READY));
83+
act(() => user2Client.__emitter__.emit(Event.SDK_UPDATE));
8484

8585
// SplitContext renders 3 times: initially, when ready from cache, and when ready.
8686
expect(countSplitContext).toEqual(3);

0 commit comments

Comments
 (0)