Skip to content

Commit d908180

Browse files
Merge pull request #184 from splitio/development
Release v1.11.0
2 parents 06df6d7 + 5fa09ae commit d908180

34 files changed

+765
-214
lines changed

.github/workflows/ci.yml renamed to .github/workflows/ci-cd.yml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -23,14 +23,14 @@ jobs:
2323
runs-on: ubuntu-latest
2424
steps:
2525
- name: Checkout code
26-
uses: actions/checkout@v3
26+
uses: actions/checkout@v4
2727
with:
2828
fetch-depth: 0
2929

3030
- name: Set up nodejs
3131
uses: actions/setup-node@v3
3232
with:
33-
node-version: '16.16.0'
33+
node-version: 'lts/*'
3434
cache: 'npm'
3535

3636
- name: npm ci

.github/workflows/update-license-year.yml

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,7 +13,7 @@ jobs:
1313
runs-on: ubuntu-latest
1414
steps:
1515
- name: Checkout
16-
uses: actions/checkout@v3
16+
uses: actions/checkout@v4
1717
with:
1818
fetch-depth: 0
1919

@@ -24,7 +24,7 @@ jobs:
2424
run: "echo PREVIOUS=$(($CURRENT-1)) >> $GITHUB_ENV"
2525

2626
- name: Update LICENSE
27-
uses: jacobtomlinson/gha-find-replace@v2
27+
uses: jacobtomlinson/gha-find-replace@v3
2828
with:
2929
find: ${{ env.PREVIOUS }}
3030
replace: ${{ env.CURRENT }}
@@ -38,7 +38,7 @@ jobs:
3838
git commit -m "Updated License Year" -a
3939
4040
- name: Create Pull Request
41-
uses: peter-evans/create-pull-request@v3
41+
uses: peter-evans/create-pull-request@v5
4242
with:
4343
token: ${{ secrets.GITHUB_TOKEN }}
4444
title: Update License Year

.nvmrc

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
v16.16.0
1+
lts/*

CHANGES.txt

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,10 @@
1+
1.11.0 (January 16, 2023)
2+
- Added the new `SplitFactoryProvider` component as a replacement for the now deprecated `SplitFactory` component.
3+
The new component is a revised version of `SplitFactory`, addressing improper handling of the SDK initialization side-effects in the `componentDidMount` and `componentDidUpdate` methods (commit phase), causing some issues like memory leaks and the SDK not reinitializing when component props change (Related to issue #11 and #148).
4+
The `SplitFactoryProvider` component can be used as a drop-in replacement for `SplitFactory`. It utilizes the React Hooks API, that requires React 16.8.0 or later, and supports server-side rendering. See our documentation for more details (Related to issue #11 and #109).
5+
- Updated internal code to remove a circular dependency and avoid warning messages with tools like PNPM (Related to issue #176).
6+
- Updated @splitsoftware/splitio package to version 10.25.1 for vulnerability fixes.
7+
18
1.10.2 (December 12, 2023)
29
- Updated @splitsoftware/splitio package to version 10.24.1 that updates localStorage usage to clear cached feature flag definitions before initiating the synchronization process, if the cache was previously synchronized with a different SDK key (i.e., a different environment) or different Split Filter criteria, to avoid using invalid cached data when the SDK is ready from cache.
310

@@ -15,6 +22,7 @@
1522
- `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.
1623
- 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).
1724
- Added TypeScript types and interfaces to the library index exports, allowing them to be imported from the library index, e.g., `import type { ISplitFactoryProps } from '@splitsoftware/splitio-react'` (Related to issue https://github.com/splitio/react-client/issues/162).
25+
- Added `defaultTreatment` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager (Related to issue https://github.com/splitio/javascript-commons/issues/225).
1826
- 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).
1927
- Updated linter and other dependencies for vulnerability fixes.
2028
- 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.

LICENSE

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
Copyright © 2023 Split Software, Inc.
1+
Copyright © 2024 Split Software, Inc.
22

33
Licensed under the Apache License, Version 2.0 (the "License");
44
you may not use this file except in compliance with the License.

README.md

Lines changed: 4 additions & 4 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, useSplitTreatments } from '@splitsoftware/splitio-react';
23+
import { SplitFactoryProvider, useSplitTreatments } from '@splitsoftware/splitio-react';
2424

2525
// Define your config object
2626
const CONFIG = {
@@ -48,10 +48,10 @@ function MyComponent() {
4848

4949
function MyApp() {
5050
return (
51-
// Use SplitFactory to instantiate the SDK and makes it available to nested components
52-
<SplitFactory config={CONFIG} >
51+
// Use SplitFactoryProvider to instantiate the SDK and makes it available to nested components
52+
<SplitFactoryProvider config={CONFIG} >
5353
<MyComponent />
54-
</SplitFactory>
54+
</SplitFactoryProvider>
5555
);
5656
}
5757
```

package-lock.json

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

package.json

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@splitsoftware/splitio-react",
3-
"version": "1.10.2",
3+
"version": "1.11.0",
44
"description": "A React library to easily integrate and use Split JS SDK",
55
"main": "lib/index.js",
66
"module": "es/index.js",
@@ -62,7 +62,7 @@
6262
},
6363
"homepage": "https://github.com/splitio/react-client#readme",
6464
"dependencies": {
65-
"@splitsoftware/splitio": "10.24.1",
65+
"@splitsoftware/splitio": "10.25.1",
6666
"memoize-one": "^5.1.1",
6767
"shallowequal": "^1.1.0"
6868
},

src/SplitClient.tsx

Lines changed: 3 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,12 @@
11
import React from 'react';
22
import { SplitContext } from './SplitContext';
33
import { ISplitClientProps, ISplitContextValues, IUpdateProps } from './types';
4-
import { ERROR_SC_NO_FACTORY } from './constants';
54
import { getStatus, getSplitClient, initAttributes, IClientWithContext } from './utils';
65
import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
76

87
/**
98
* Common component used to handle the status and events of a Split client passed as prop.
10-
* Reused by both SplitFactory (main client) and SplitClient (shared client) components.
9+
* Reused by both SplitFactoryProvider (main client) and SplitClient (any client) components.
1110
*/
1211
export class SplitComponent extends React.Component<IUpdateProps & { factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null, attributes?: SplitIO.Attributes, children: any }, ISplitContextValues> {
1312

@@ -47,11 +46,6 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
4746
super(props);
4847
const { factory, client } = props;
4948

50-
// Log error if factory is not available
51-
if (!factory) {
52-
console.error(ERROR_SC_NO_FACTORY);
53-
}
54-
5549
this.state = {
5650
factory,
5751
client,
@@ -129,9 +123,8 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
129123
* SplitClient will initialize a new SDK client and listen for its events in order to update the Split Context.
130124
* Children components will have access to the new client when accessing Split Context.
131125
*
132-
* Unlike SplitFactory, the underlying SDK client can be changed during the component lifecycle
133-
* if the component is updated with a different splitKey or trafficType prop. Since the client can change,
134-
* its release is not handled by SplitClient but by its container SplitFactory component.
126+
* The underlying SDK client can be changed during the component lifecycle
127+
* if the component is updated with a different splitKey or trafficType prop.
135128
*
136129
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#advanced-instantiate-multiple-sdk-clients}
137130
*/

src/SplitContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,6 @@ export const INITIAL_CONTEXT: ISplitContextValues = {
1818
/**
1919
* Split Context is the React Context instance that represents our SplitIO global state.
2020
* It contains Split SDK objects, such as a factory instance, a client and its status (isReady, isTimedout, lastUpdate)
21-
* The context is created with default empty values, that eventually SplitFactory and SplitClient access and update.
21+
* The context is created with default empty values, that SplitFactoryProvider and SplitClient access and update.
2222
*/
2323
export const SplitContext = React.createContext<ISplitContextValues>(INITIAL_CONTEXT);

src/SplitFactory.tsx

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,12 +9,17 @@ import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
99
/**
1010
* SplitFactory will initialize the Split SDK and its main client, listen for its events in order to update the Split Context,
1111
* and automatically shutdown and release resources when it is unmounted. SplitFactory must wrap other components and functions
12-
* from this library, since they access the Split Context and its elements (factory, clients, etc).
12+
* from this library, since they access the Split Context and its properties (factory, client, isReady, etc).
1313
*
1414
* The underlying SDK factory and client is set on the constructor, and cannot be changed during the component lifecycle,
1515
* even if the component is updated with a different config or factory prop.
1616
*
17-
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK}
17+
* @deprecated Replace with the new `SplitFactoryProvider` component.
18+
* `SplitFactoryProvider` is a drop-in replacement that properly handles side effects (factory creation and destruction) within the React component lifecycle, avoiding issues with factory recreation and memory leaks.
19+
* Note: There is a subtle breaking change in `SplitFactoryProvider`. When using the `config` prop, `factory` and `client` properties in the context are `null` in the first render, until the context is updated when some event is emitted on
20+
* the SDK main client (ready, ready from cache, timeout or update depending on the configuration of the `updateOnXXX` props of the component). This differs from the previous behavior where `factory` and `client` were immediately available.
21+
*
22+
* @see {@link https://help.split.io/hc/en-us/articles/360038825091-React-SDK#2-instantiate-the-sdk-and-create-a-new-split-client}
1823
*/
1924
export class SplitFactory extends React.Component<ISplitFactoryProps, { factory: SplitIO.IBrowserSDK | null, client: SplitIO.IBrowserClient | null }> {
2025

src/SplitFactoryProvider.tsx

Lines changed: 86 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,86 @@
1+
import React from 'react';
2+
3+
import { SplitComponent } from './SplitClient';
4+
import { ISplitFactoryProps } from './types';
5+
import { WARN_SF_CONFIG_AND_FACTORY } from './constants';
6+
import { getSplitFactory, destroySplitFactory, IFactoryWithClients, getSplitClient, getStatus, __factories } from './utils';
7+
import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
8+
9+
/**
10+
* SplitFactoryProvider will initialize the Split SDK and its main client when `config` prop is provided or updated, listen for its events in order to update the Split Context,
11+
* and automatically destroy the SDK (shutdown and release resources) when it is unmounted or `config` prop updated. SplitFactoryProvider must wrap other library components and
12+
* functions since they access the Split Context and its properties (factory, client, isReady, etc).
13+
*
14+
* NOTE: Either pass a factory instance or a config object. If both are passed, the config object will be ignored.
15+
* Pass the same reference to the config or factory object rather than a new instance on each render, to avoid unnecessary props changes and SDK reinitializations.
16+
*
17+
* @see {@link https://help.split.io/hc/en-us/articles/360038825091-React-SDK#2-instantiate-the-sdk-and-create-a-new-split-client}
18+
*/
19+
export function SplitFactoryProvider(props: ISplitFactoryProps) {
20+
let {
21+
config, factory: propFactory,
22+
updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate
23+
} = { ...DEFAULT_UPDATE_OPTIONS, ...props };
24+
25+
if (config && propFactory) {
26+
console.log(WARN_SF_CONFIG_AND_FACTORY);
27+
config = undefined;
28+
}
29+
30+
const [configFactory, setConfigFactory] = React.useState<IFactoryWithClients | null>(null);
31+
const factory = propFactory || (configFactory && config === configFactory.config ? configFactory : null);
32+
const client = factory ? getSplitClient(factory) : null;
33+
34+
// Effect to initialize and destroy the factory
35+
React.useEffect(() => {
36+
if (config) {
37+
const factory = getSplitFactory(config);
38+
39+
return () => {
40+
destroySplitFactory(factory);
41+
}
42+
}
43+
}, [config]);
44+
45+
// Effect to subscribe/unsubscribe to events
46+
React.useEffect(() => {
47+
const factory = config && __factories.get(config);
48+
if (factory) {
49+
const client = getSplitClient(factory);
50+
const status = getStatus(client);
51+
52+
// Unsubscribe from events and update state when first event is emitted
53+
const update = () => { // eslint-disable-next-line no-use-before-define
54+
unsubscribe();
55+
setConfigFactory(factory);
56+
}
57+
58+
const unsubscribe = () => {
59+
client.off(client.Event.SDK_READY, update);
60+
client.off(client.Event.SDK_READY_FROM_CACHE, update);
61+
client.off(client.Event.SDK_READY_TIMED_OUT, update);
62+
client.off(client.Event.SDK_UPDATE, update);
63+
}
64+
65+
if (updateOnSdkReady) {
66+
if (status.isReady) update();
67+
else client.once(client.Event.SDK_READY, update);
68+
}
69+
if (updateOnSdkReadyFromCache) {
70+
if (status.isReadyFromCache) update();
71+
else client.once(client.Event.SDK_READY_FROM_CACHE, update);
72+
}
73+
if (updateOnSdkTimedout) {
74+
if (status.hasTimedout) update();
75+
else client.once(client.Event.SDK_READY_TIMED_OUT, update);
76+
}
77+
if (updateOnSdkUpdate) client.on(client.Event.SDK_UPDATE, update);
78+
79+
return unsubscribe;
80+
}
81+
}, [config, updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate]);
82+
83+
return (
84+
<SplitComponent {...props} factory={factory} client={client} />
85+
);
86+
}

0 commit comments

Comments
 (0)