Skip to content

Commit 8345e49

Browse files
Merge branch 'hooks_update_options' into hooks_polishing
2 parents 91e4cc1 + 07cceca commit 8345e49

25 files changed

Lines changed: 375 additions & 380 deletions

.eslintrc.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,8 @@ module.exports = {
77
'extends': [
88
'eslint:recommended',
99
'plugin:react/recommended',
10-
'plugin:@typescript-eslint/recommended'
10+
'plugin:@typescript-eslint/recommended',
11+
'plugin:react-hooks/recommended'
1112
],
1213
'parser': '@typescript-eslint/parser',
1314
'parserOptions': {

.github/workflows/ci.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -43,7 +43,7 @@ jobs:
4343
run: npm run test -- --coverage
4444

4545
- name: npm build
46-
run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") BUILD_COMMIT=${{ github.sha }} npm run build
46+
run: BUILD_BRANCH=$(echo "${GITHUB_REF#refs/heads/}") npm run build
4747

4848
- name: Set VERSION env
4949
run: echo "VERSION=$(cat package.json | jq -r .version)" >> $GITHUB_ENV

CHANGES.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,11 @@
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.
3+
4+
1.9.0 (July 18, 2023)
5+
- Updated some transitive dependencies for vulnerability fixes.
6+
- Updated @splitsoftware/splitio package to version 10.23.0 that includes:
7+
- Updated streaming architecture implementation to apply feature flag updates from the notification received which is now enhanced, improving efficiency and reliability of the whole update system.
8+
19
1.8.3 (May 16, 2023)
210
- Updated @splitsoftware/splitio package to version 10.22.5 that includes:
311
- Updated terminology on the SDKs codebase to be more aligned with current standard without causing a breaking change. The core change is the term split for feature flag on things like logs and IntelliSense comments.

package-lock.json

Lines changed: 238 additions & 168 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 & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-react",
3-
"version": "1.8.3",
3+
"version": "1.9.0",
44
"description": "A React library to easily integrate and use Split JS SDK",
55
"main": "lib/index.js",
66
"module": "es/index.js",
@@ -19,7 +19,7 @@
1919
"scripts": {
2020
"build:cjs": "rimraf lib/* types/* && tsc -m commonjs --outDir lib -d true --declarationDir types",
2121
"build:esm": "rimraf es/* && tsc",
22-
"build:umd": "rimraf umd/* && webpack --config webpack.dev.js --env branch=$BUILD_BRANCH --env commit_hash=$BUILD_COMMIT && webpack --config webpack.prod.js --env branch=$BUILD_BRANCH --env commit_hash=$BUILD_COMMIT",
22+
"build:umd": "rimraf umd/* && webpack --config webpack.dev.js --env branch=$BUILD_BRANCH && webpack --config webpack.prod.js --env branch=$BUILD_BRANCH",
2323
"build": "npm run build:cjs && npm run build:esm && npm run build:umd",
2424
"postbuild": "replace 'REACT_SDK_VERSION_NUMBER' $npm_package_version ./lib/constants.js ./es/constants.js ./umd -r",
2525
"check": "npm run check:lint && npm run check:types",
@@ -62,7 +62,7 @@
6262
},
6363
"homepage": "https://github.com/splitio/react-client#readme",
6464
"dependencies": {
65-
"@splitsoftware/splitio": "10.22.5",
65+
"@splitsoftware/splitio": "10.23.0",
6666
"memoize-one": "^5.1.1",
6767
"shallowequal": "^1.1.0"
6868
},
@@ -81,6 +81,7 @@
8181
"eslint-plugin-compat": "^4.1.2",
8282
"eslint-plugin-import": "^2.27.5",
8383
"eslint-plugin-react": "^7.32.2",
84+
"eslint-plugin-react-hooks": "^4.6.0",
8485
"husky": "^3.1.0",
8586
"jest": "^27.2.3",
8687
"react": "^18.0.0",

src/SplitClient.tsx

Lines changed: 6 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { SplitContext } from './SplitContext';
33
import { ISplitClientProps, ISplitContextValues, IUpdateProps } from './types';
44
import { ERROR_SC_NO_FACTORY } from './constants';
5-
import { getStatus, getSplitClient, initAttributes } from './utils';
5+
import { getStatus, getSplitClient, initAttributes, IClientWithContext } from './utils';
66

77
/**
88
* Common component used to handle the status and events of a Split client passed as prop.
@@ -58,7 +58,6 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
5858
factory,
5959
client,
6060
...getStatus(client),
61-
lastUpdate: 0,
6261
};
6362
}
6463

@@ -83,21 +82,20 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
8382
}
8483
}
8584

86-
// NOTE: assuming that SDK events are scattered in time so that Date.now() timestamps are unique per event and trigger an update
8785
setReady = () => {
88-
if (this.props.updateOnSdkReady) this.setState({ lastUpdate: Date.now() });
86+
if (this.props.updateOnSdkReady) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
8987
}
9088

9189
setReadyFromCache = () => {
92-
if (this.props.updateOnSdkReadyFromCache) this.setState({ lastUpdate: Date.now() });
90+
if (this.props.updateOnSdkReadyFromCache) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
9391
}
9492

9593
setTimedout = () => {
96-
if (this.props.updateOnSdkTimedout) this.setState({ lastUpdate: Date.now() });
94+
if (this.props.updateOnSdkTimedout) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
9795
}
9896

9997
setUpdate = () => {
100-
if (this.props.updateOnSdkUpdate) this.setState({ lastUpdate: Date.now() });
98+
if (this.props.updateOnSdkUpdate) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
10199
}
102100

103101
componentDidMount() {
@@ -112,7 +110,7 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
112110
}
113111

114112
componentWillUnmount() {
115-
// unsubscrite to SDK client events, to remove references to SplitClient instance methods
113+
// unsubscribe from events, to remove references to SplitClient instance methods
116114
this.unsubscribeFromEvents(this.props.client);
117115
}
118116

src/__tests__/SplitClient.test.tsx

Lines changed: 7 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { render, act } from '@testing-library/react';
33

44
/** Mocks and test utils */
5-
import { mockSdk, Event, assertNoListeners, clientListenerCount } from './testUtils/mockSplitSdk';
5+
import { mockSdk, Event } from './testUtils/mockSplitSdk';
66
jest.mock('@splitsoftware/splitio/client', () => {
77
return { SplitFactory: mockSdk() };
88
});
@@ -20,30 +20,22 @@ import { testAttributesBinding, TestComponentProps } from './testUtils/utils';
2020
describe('SplitClient', () => {
2121

2222
test('passes no-ready props to the child if client is not ready.', () => {
23-
let clientToCheck;
24-
2523
render(
2624
<SplitFactory config={sdkBrowser} >
2725
<SplitClient splitKey='user1' >
28-
{({ client, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => {
26+
{({ isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => {
2927
expect(isReady).toBe(false);
3028
expect(isReadyFromCache).toBe(false);
3129
expect(hasTimedout).toBe(false);
3230
expect(isTimedout).toBe(false);
3331
expect(isDestroyed).toBe(false);
3432
expect(lastUpdate).toBe(0);
3533

36-
clientToCheck = client;
3734
return null;
3835
}}
3936
</SplitClient>
4037
</SplitFactory>
4138
);
42-
43-
// After component mount, listeners should be attached for all ONCE events when none of them has been emitted yet
44-
expect(clientListenerCount(clientToCheck, Event.SDK_READY)).toBe(1);
45-
expect(clientListenerCount(clientToCheck, Event.SDK_READY_FROM_CACHE)).toBe(1);
46-
expect(clientListenerCount(clientToCheck, Event.SDK_READY_TIMED_OUT)).toBe(1);
4739
});
4840

4941
test('passes ready props to the child if client is ready.', async () => {
@@ -52,7 +44,6 @@ describe('SplitClient', () => {
5244
(outerFactory as any).client().__emitter__.emit(Event.SDK_READY);
5345
(outerFactory.manager().names as jest.Mock).mockReturnValue(['split1']);
5446
await outerFactory.client().ready();
55-
let clientToCheck;
5647

5748
render(
5849
<SplitFactory factory={outerFactory} >
@@ -66,17 +57,11 @@ describe('SplitClient', () => {
6657
expect(isDestroyed).toBe(false);
6758
expect(lastUpdate).toBe(0);
6859

69-
clientToCheck = client;
7060
return null;
7161
}}
7262
</SplitClient>
7363
</SplitFactory>
7464
);
75-
76-
// After component mount, listeners should not be attached for those ONCE events that has been already emitted
77-
expect(clientListenerCount(clientToCheck, Event.SDK_READY)).toBe(0);
78-
expect(clientListenerCount(clientToCheck, Event.SDK_READY_FROM_CACHE)).toBe(0);
79-
expect(clientListenerCount(clientToCheck, Event.SDK_READY_TIMED_OUT)).toBe(0); // this event was not emitted, but will not anyway, since SDK_READY has.
8065
});
8166

8267
test('rerender child on SDK_READY_TIMEDOUT, SDK_READY_FROM_CACHE, SDK_READY and SDK_UPDATE events.', async () => {
@@ -89,7 +74,7 @@ describe('SplitClient', () => {
8974
let renderTimes = 0;
9075
let previousLastUpdate = -1;
9176

92-
const wrapper = render(
77+
render(
9378
<SplitFactory factory={outerFactory} >
9479
<SplitClient splitKey='user2' updateOnSdkTimedout={true} updateOnSdkUpdate={true} >
9580
{({ client, isReady, isReadyFromCache, hasTimedout, isTimedout, lastUpdate }: ISplitClientChildProps) => {
@@ -114,8 +99,7 @@ describe('SplitClient', () => {
11499
fail('Child must not be rerendered');
115100
}
116101
expect(client).toBe(outerFactory.client('user2'));
117-
expect(lastUpdate).toBeGreaterThanOrEqual(previousLastUpdate);
118-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
102+
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
119103
renderTimes++;
120104
previousLastUpdate = lastUpdate;
121105
return null;
@@ -130,10 +114,6 @@ describe('SplitClient', () => {
130114
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_UPDATE));
131115

132116
expect(renderTimes).toBe(5);
133-
134-
// check that outerFactory's clients have no event listeners
135-
wrapper.unmount();
136-
assertNoListeners(outerFactory);
137117
});
138118

139119
test('rerender child on SDK_READY_TIMED_OUT and SDK_UPDATE events, but not on SDK_READY.', async () => {
@@ -146,7 +126,7 @@ describe('SplitClient', () => {
146126
let renderTimes = 0;
147127
let previousLastUpdate = -1;
148128

149-
const wrapper = render(
129+
render(
150130
<SplitFactory factory={outerFactory} >
151131
<SplitClient splitKey='user2' updateOnSdkReady={false} updateOnSdkTimedout={true} updateOnSdkUpdate={true} >
152132
{({ client, isReady, isReadyFromCache, hasTimedout, isTimedout, lastUpdate }: ISplitClientChildProps) => {
@@ -165,8 +145,7 @@ describe('SplitClient', () => {
165145
fail('Child must not be rerendered');
166146
}
167147
expect(client).toBe(outerFactory.client('user2'));
168-
expect(lastUpdate).toBeGreaterThanOrEqual(previousLastUpdate);
169-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
148+
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
170149
renderTimes++;
171150
previousLastUpdate = lastUpdate;
172151
return null;
@@ -179,10 +158,6 @@ describe('SplitClient', () => {
179158
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_READY));
180159
act(() => (outerFactory as any).client('user2').__emitter__.emit(Event.SDK_UPDATE));
181160
expect(renderTimes).toBe(3);
182-
183-
// check that outerFactory's clients have no event listeners
184-
wrapper.unmount();
185-
assertNoListeners(outerFactory);
186161
});
187162

188163
test('rerender child only on SDK_READY event, as default behaviour.', async () => {
@@ -212,7 +187,6 @@ describe('SplitClient', () => {
212187
}
213188
expect(client).toBe(outerFactory.client('user2'));
214189
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
215-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
216190
renderTimes++;
217191
previousLastUpdate = lastUpdate;
218192
return null;
@@ -299,10 +273,6 @@ describe('SplitClient', () => {
299273
setTimeout(() => {
300274
expect(renderTimes).toBe(6);
301275

302-
// check that outerFactory's clients have no event listeners
303-
// eslint-disable-next-line no-use-before-define
304-
wrapper.unmount();
305-
assertNoListeners(outerFactory);
306276
done();
307277
});
308278
});
@@ -359,7 +329,7 @@ describe('SplitClient', () => {
359329
}
360330
}
361331

362-
const wrapper = render(
332+
render(
363333
<SplitFactory factory={outerFactory} >
364334
<InnerComponent />
365335
</SplitFactory>

src/__tests__/SplitFactory.test.tsx

Lines changed: 7 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { render, act } from '@testing-library/react';
33

44
/** Mocks */
5-
import { mockSdk, Event, assertNoListeners } from './testUtils/mockSplitSdk';
5+
import { mockSdk, Event } from './testUtils/mockSplitSdk';
66
jest.mock('@splitsoftware/splitio/client', () => {
77
return { SplitFactory: mockSdk() };
88
});
@@ -67,7 +67,7 @@ describe('SplitFactory', () => {
6767
let renderTimes = 0;
6868
let previousLastUpdate = -1;
6969

70-
const wrapper = render(
70+
render(
7171
<SplitFactory factory={outerFactory} updateOnSdkTimedout={true} updateOnSdkUpdate={true} >
7272
{({ factory, isReady, isReadyFromCache, hasTimedout, isTimedout, lastUpdate }: ISplitFactoryChildProps) => {
7373
const statusProps = [isReady, isReadyFromCache, hasTimedout, isTimedout];
@@ -91,8 +91,7 @@ describe('SplitFactory', () => {
9191
fail('Child must not be rerendered');
9292
}
9393
expect(factory).toBe(outerFactory);
94-
expect(lastUpdate).toBeGreaterThanOrEqual(previousLastUpdate);
95-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
94+
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
9695
renderTimes++;
9796
previousLastUpdate = lastUpdate;
9897
return null;
@@ -104,19 +103,16 @@ describe('SplitFactory', () => {
104103
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE));
105104
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_READY));
106105
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE));
107-
expect(renderTimes).toBe(5);
108106

109-
// check that outerFactory's clients have no event listeners
110-
wrapper.unmount();
111-
assertNoListeners(outerFactory);
107+
expect(renderTimes).toBe(5);
112108
});
113109

114110
test('rerenders child on SDK_READY_TIMED_OUT and SDK_UPDATE events, but not on SDK_READY.', async () => {
115111
const outerFactory = SplitSdk(sdkBrowser);
116112
let renderTimes = 0;
117113
let previousLastUpdate = -1;
118114

119-
const wrapper = render(
115+
render(
120116
<SplitFactory factory={outerFactory} updateOnSdkReady={false} updateOnSdkTimedout={true} updateOnSdkUpdate={true} >
121117
{({ factory, isReady, isReadyFromCache, hasTimedout, isTimedout, lastUpdate }: ISplitFactoryChildProps) => {
122118
const statusProps = [isReady, isReadyFromCache, hasTimedout, isTimedout];
@@ -134,8 +130,7 @@ describe('SplitFactory', () => {
134130
fail('Child must not be rerendered');
135131
}
136132
expect(factory).toBe(outerFactory);
137-
expect(lastUpdate).toBeGreaterThanOrEqual(previousLastUpdate);
138-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
133+
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
139134
renderTimes++;
140135
previousLastUpdate = lastUpdate;
141136
return null;
@@ -146,11 +141,8 @@ describe('SplitFactory', () => {
146141
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT));
147142
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_READY));
148143
act(() => (outerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE));
149-
expect(renderTimes).toBe(3);
150144

151-
// check that outerFactory's clients have no event listeners
152-
wrapper.unmount();
153-
assertNoListeners(outerFactory);
145+
expect(renderTimes).toBe(3);
154146
});
155147

156148
test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behaviour.', async () => {
@@ -174,7 +166,6 @@ describe('SplitFactory', () => {
174166
}
175167
expect(factory).toBe(outerFactory);
176168
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
177-
expect(lastUpdate).toBeLessThanOrEqual(Date.now());
178169
renderTimes++;
179170
previousLastUpdate = lastUpdate;
180171
return null;

0 commit comments

Comments
 (0)