Skip to content

Commit 6e3738f

Browse files
Merge pull request #169 from splitio/hooks_baseline
Prepare release v1.10.0
2 parents c8333bd + 45c9b7e commit 6e3738f

50 files changed

Lines changed: 1218 additions & 1123 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.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': {

.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: 14 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,18 @@
1-
1.9.1 (October XX, 2023)
2-
- Updated type definitions 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).
1+
1.10.0 (November 16, 2023)
2+
- Added support for Flag Sets on the SDK, which enables grouping feature flags and interacting with the group rather than individually (more details in our documentation):
3+
- Added a new `flagSets` prop to the `SplitTreatments` component and `flagSets` option to the `useSplitTreatments` hook options object, to support evaluating flags in given flag set/s. Either `names` or `flagSets` must be provided to the component and hook. If both are provided, `names` will be used.
4+
- Added a new optional Split Filter configuration option. This allows the SDK and Split services to only synchronize the flags in the specified flag sets, avoiding unused or unwanted flags from being synced on the SDK instance, bringing all the benefits from a reduced payload.
5+
- Added `sets` property to the `SplitView` object returned by the `split` and `splits` methods of the SDK manager to expose flag sets on flag views.
6+
- Added new `useSplitClient`, `useSplitTreatments` and `useSplitManager` hooks as replacements for the now deprecated `useClient`, `useTreatments` and `useManager` hooks.
7+
- 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.
8+
- `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`.
9+
- `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.
10+
- 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).
11+
- 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).
12+
- 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).
313
- Updated linter and other dependencies for vulnerability fixes.
14+
- 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.
15+
- 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).
416

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

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: 43 additions & 23 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 3 additions & 2 deletions
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.10.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.23.0",
65+
"@splitsoftware/splitio": "10.24.0-beta",
6666
"memoize-one": "^5.1.1",
6767
"shallowequal": "^1.1.0"
6868
},
@@ -81,6 +81,7 @@
8181
"eslint-plugin-compat": "^4.2.0",
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: 10 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,8 @@ 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, getSplitSharedClient, initAttributes } from './utils';
5+
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,13 +12,10 @@ import { getStatus, getSplitSharedClient, initAttributes } from './utils';
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.
@@ -58,7 +56,6 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
5856
factory,
5957
client,
6058
...getStatus(client),
61-
lastUpdate: 0,
6259
};
6360
}
6461

@@ -83,21 +80,20 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
8380
}
8481
}
8582

86-
// NOTE: assuming that SDK events are scatered enough in time, so that Date.now() result is unique per event and triggers an update
8783
setReady = () => {
88-
if (this.props.updateOnSdkReady) this.setState({ lastUpdate: Date.now() });
84+
if (this.props.updateOnSdkReady) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
8985
}
9086

9187
setReadyFromCache = () => {
92-
if (this.props.updateOnSdkReadyFromCache) this.setState({ lastUpdate: Date.now() });
88+
if (this.props.updateOnSdkReadyFromCache) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
9389
}
9490

9591
setTimedout = () => {
96-
if (this.props.updateOnSdkTimedout) this.setState({ lastUpdate: Date.now() });
92+
if (this.props.updateOnSdkTimedout) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
9793
}
9894

9995
setUpdate = () => {
100-
if (this.props.updateOnSdkUpdate) this.setState({ lastUpdate: Date.now() });
96+
if (this.props.updateOnSdkUpdate) this.setState({ lastUpdate: (this.state.client as IClientWithContext).lastUpdate });
10197
}
10298

10399
componentDidMount() {
@@ -112,7 +108,7 @@ export class SplitComponent extends React.Component<IUpdateProps & { factory: Sp
112108
}
113109

114110
componentWillUnmount() {
115-
// unsubscrite to SDK client events, to remove references to SplitClient instance methods
111+
// unsubscribe from events, to remove references to SplitClient instance methods
116112
this.unsubscribeFromEvents(this.props.client);
117113
}
118114

@@ -145,8 +141,8 @@ export function SplitClient(props: ISplitClientProps) {
145141
<SplitContext.Consumer>
146142
{(splitContext: ISplitContextValues) => {
147143
const { factory } = splitContext;
148-
// getSplitSharedClient is idempotent like factory.client: it returns the same client given the same factory, Split Key and TT
149-
const client = factory ? getSplitSharedClient(factory, props.splitKey, props.trafficType) : null;
144+
// getSplitClient is idempotent like factory.client: it returns the same client given the same factory, Split Key and TT
145+
const client = factory ? getSplitClient(factory, props.splitKey, props.trafficType) : null;
150146
return (
151147
<SplitComponent {...props} factory={factory} client={client} attributes={props.attributes} />
152148
);

src/SplitFactory.tsx

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,8 @@ import React from 'react';
33
import { SplitComponent } from './SplitClient';
44
import { ISplitFactoryProps } from './types';
55
import { WARN_SF_CONFIG_AND_FACTORY, ERROR_SF_NO_CONFIG_AND_FACTORY } from './constants';
6-
import { getSplitFactory, destroySplitFactory, IFactoryWithClients } from './utils';
6+
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 } from './uti
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 }>;
@@ -54,7 +52,7 @@ export class SplitFactory extends React.Component<ISplitFactoryProps, { factory:
5452
this.isFactoryExternal = propFactory ? true : false;
5553

5654
// Instantiate main client. Attributes are set on `SplitComponent.getDerivedStateFromProps`
57-
const client = factory ? factory.client() : null;
55+
const client = factory ? getSplitClient(factory) : null;
5856

5957
this.state = {
6058
client,

0 commit comments

Comments
 (0)