React SDK v2.0.0 has a few breaking changes that you should consider when migrating from a previous version. The main changes are:
Follow this section to migrate to the new hooks useSplitClient
, useSplitTreatments
, and useSplitManager
.
• Deprecated SplitFactory
provider has been removed, withSplitFactory
is deprecated, and SplitFactoryProvider
doesn't accept updateOn
props and a render function as children anymore.
To migrate your existing code to the new version of SplitFactoryProvider
, consider the following refactor example:
const MyComponent = (props: ISplitContextValues) => {
const { factory, client, isReady, isReadyFromCache, ... } = props;
...
};
// if using SplitFactoryProvider v1.11.0
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} updateOnSdkUpdate={false} attributes={DEFAULT_CLIENT_ATTRIBUTES} >
{MyComponent}
</SplitFactoryProvider>
);
};
// or SplitFactory
const App = () => {
return (
<SplitFactory config={mySplitConfig} updateOnSdkUpdate={false} attributes={DEFAULT_CLIENT_ATTRIBUTES} >
{MyComponent}
</SplitFactory>
);
};
// or withSplitFactory
const App = withSplitFactory(mySplitConfig, undefined, DEFAULT_CLIENT_ATTRIBUTES)(
MyComponent, false /* updateOnSdkUpdate = false */
);
should be refactored to:
const MyComponent = () => {
const props: ISplitContextValues = useSplitClient({ updateOnSdkUpdate: false });
const { factory, client, isReady, isReadyFromCache, ... } = props;
...
};
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} attributes={DEFAULT_CLIENT_ATTRIBUTES} >
<MyComponent />
</SplitFactoryProvider>
);
};
Notice that MyComponent
was refactored to use the useSplitClient
hook and is passed as a React JSX element rather than a render function. The useSplitClient
hook is called without providing a splitKey
param. This means that the default client (whose key is set in the core.key
property of the mySplitConfig
object) will be used, and the updateOn
and attributes
props are passed as options to the hook.
• High-Order-Components (withSplitClient
, withSplitTreatments
) and components that accept a render function as child component (SplitTreatments
, and SplitClient
) have been deprecated and might be removed in a future major release.
The deprecation is intended to simplify the API and discourage using old patterns (HOCs and render props) in favor of the hook alternatives, to take advantage of React optimizations.
To migrate your existing code based on withSplitClient
or SplitClient
, consider the following refactor using the useSplitClient
hook:
const MyComponent = (props: ISplitContextValues) => {
const { client, isReady, ... } = props;
...
};
const App = withSplitFactory(mySplitConfig)(
withSplitClient(OTHER_KEY, OTHER_KEY_ATTRIBUTES)(
MyComponent, undefined, undefined, undefined, false /* updateOnSdkReadyFromCache = false */
)
);
// or
const App = () => {
return (
<SplitFactory config={mySplitConfig} >
<SplitClient splitKey={OTHER_KEY} attributes={OTHER_KEY_ATTRIBUTES} updateOnSdkReadyFromCache={false} >
{MyComponent}
</SplitClient>
</SplitFactory>
)
};
should be refactored to:
const MyComponent = () => {
const props: ISplitContextValues = useSplitClient({ splitKey: OTHER_KEY, attributes: OTHER_KEY_ATTRIBUTES, updateOnSdkReadyFromCache: false });
const { client, isReady, ... } = props;
...
};
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} >
<MyComponent />
</SplitFactory>
)
};
To migrate your existing code based on withSplitTreatments
or SplitTreatments
, consider the following refactor using the useSplitTreatments
hook:
const MyComponent = (props: ISplitTreatmentsChildProps) => {
const { treatments, isReady, ... } = props;
...
};
const App = withSplitFactory(mySplitConfig)(
withSplitClient(OTHER_KEY)(
withSplitTreatments(FEATURE_FLAG_NAMES, ATTRIBUTES)(
MyComponent
)
)
);
// or
const App = () => {
return (
<SplitFactory config={mySplitConfig} >
<SplitClient splitKey={OTHER_KEY} >
<SplitTreatments names={FEATURE_FLAG_NAMES} attributes={ATTRIBUTES} >
{MyComponent}
</SplitTreatments>
</SplitClient>
</SplitFactory>
)
};
should be refactored to:
const MyComponent = () => {
const props: ISplitTreatmentsChildProps = useSplitTreatments({ splitKey: OTHER_KEY, names: FEATURE_FLAG_NAMES, attributes: ATTRIBUTES });
const { treatments, isReady, ... } = props;
...
};
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} >
<MyComponent />
</SplitFactory>
)
};
If you are using the SplitSdk
function to create a factory and pass it to the SplitFactoryProvider
component, you should rename it to SplitFactory
. For example:
import { SplitSdk, SplitFactoryProvider } from '@splitsoftware/splitio-react';
const myFactory = SplitSdk(mySplitConfig);
const App = () => {
return (
<SplitFactoryProvider factory={myFactory} >
<MyComponent />
</SplitFactoryProvider>
);
};
should be refactored to:
import { SplitFactory, SplitFactoryProvider } from '@splitsoftware/splitio-react';
const myFactory = SplitFactory(mySplitConfig);
const App = () => {
return (
<SplitFactoryProvider factory={myFactory} >
<MyComponent />
</SplitFactoryProvider>
);
};
If you were passing the trafficType
to the SDK config, useSplitClient
hook, or useTrack
hook, you should remove it. The trafficType
must now be passed as the first argument of the track
method. For example:
const mySplitConfig = {
core: {
authorizationKey: YOUR_CLIENT_SIDE_SDK_KEY,
key: USER_KEY,
trafficType: 'user'
}
}
const MyComponent = () => {
const track = useTrack();
const accountTrack = useTrack(ACCOUNT_KEY, 'account');
useEffect(() => {
track('my_event');
accountTrack('my_event');
}, []);
...
};
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} >
<MyComponent />
</SplitFactory>
)
};
should be refactored to:
const mySplitConfig = {
core: {
authorizationKey: YOUR_CLIENT_SIDE_SDK_KEY,
key: USER_KEY
}
}
const MyComponent = () => {
const track = useTrack();
const accountTrack = useTrack(ACCOUNT_KEY);
useEffect(() => {
track('user', 'my_event');
accountTrack('account', 'my_event');
}, []);
...
};
const App = () => {
return (
<SplitFactoryProvider config={mySplitConfig} >
<MyComponent />
</SplitFactory>
)
};
Migrating to get React SDK v1.11.0 improvements: Replacing the deprecated SplitFactory
and withSplitFactory
components
Starting from React SDK v1.11.0, the SplitFactoryProvider
component is available and can replace the older SplitFactory
and withSplitFactory
components. The deprecated components will continue working, until they are removed in a future major release.
We recommend migrating to the new SplitFactoryProvider
component instead. This component is a revised version of SplitFactory
that properly handles SDK side effects (i.e., factory creation and destruction) within the React component lifecycle. By migrating, you can benefit from a number of improvements:
-
Resolution of memory leak issues in React development mode, strict mode, and server-side rendering.
-
Updating the SDK when
config
orfactory
props change.
Notable changes to consider when migrating:
-
SplitFactoryProvider
utilizes the React Hooks API, requiring React 16.8.0 or later, whileSplitFactory
is compatible with React 16.3.0 or later. -
When using the
config
prop withSplitFactoryProvider
, thefactory
andclient
properties inSplitContext
and themanager
property inuseSplitManager
results arenull
in the first render, until the context is updated when some event is emitted on the SDK main client (ready, ready from cache, timeout, or update, depending on the configuration of theupdateOn<Event>
props of the component). This differs from the previous behavior wherefactory
,client
, andmanager
were immediately available. Nonetheless, it is not recommended to use theclient
andfactory
properties directly as better alternatives are available. For example, use theuseTrack
anduseSplitTreatments
hooks rather than the client'strack
andgetTreatments
methods. -
Updating the
config
prop inSplitFactoryProvider
re-initializes the SDK with the new configuration, whileSplitFactory
does not reinitialize the SDK. You should pass a reference to the configuration object (e.g., via a global variable,useState
, oruseMemo
) rather than a new instance on each render, to avoid unnecessary re-initializations. -
Updating the
factory
prop inSplitFactoryProvider
replaces the current SDK instance, unlikeSplitFactory
where it is ignored.
To migrate your existing code, replace:
const MyApp = () => {
return (
<SplitFactory config={mySplitConfig}>
<MyComponent />
</SplitFactory>
);
};
or
const MyApp = withSplitFactory(mySplitConfig)(MyComponent);
with:
const MyApp = () => {
return (
<SplitFactoryProvider config={mySplitConfig}>
<MyComponent />
</SplitFactoryProvider>
);
};
and consider that factory
, client
and manager
properties might be null
until the SDK has emitted some event:
const MyComponent = () => {
// factoryFromContext === factory, clientFromContext === client, and they are null until some SDK event is emitted
const { factory: factoryFromContext, client: clientFromContext } = useContext(SplitContext);
const { factory, client } = useSplitClient();
// Example to evaluate all your flags when the SDK is ready and re-evaluate on SDK_UPDATE events
const { manager } = useSplitManager();
const FEATURE_FLAG_NAMES = manager ? manager.names() : [];
const { treatments, isReady } = useSplitTreatments({ names: FEATURE_FLAG_NAMES, updateOnSdkUpdate: true }); // updateOnSdkReady is true by default
return isReady ?
treatments['feature-flag-1'].treatment === 'on' ?
<FeatureOn /> :
<FeatureOff /> :
<LoadingPage />
}
Migrating to get React SDK v1.10.0 improvements: Replacing the deprecated useClient
, useTreatments
, and useManager
hooks
Starting from React SDK v1.10.0, the useSplitClient
, useSplitTreatments
, and useSplitManager
hooks are available and can replace the older useClient
, useTreatments
, and useManager
hooks respectively. The deprecated hooks will continue working, until they are removed in a future major release.
We recommend migrating to the new versions useSplitClient
, useSplitTreatments
and useSplitManager
respectively, which provide a more flexible API:
-
They accept an options object as parameter, instead of a list of parameters as their deprecated counterparts. The options object can contain the same parameters as the old hooks, plus some extra optional parameters:
updateOnSdkReady
,updateOnSdkReadyFromCache
,updateOnSdkTimedout
, andupdateOnSdkUpdate
, which control when the hook updates the component. For example, you can setupdateOnSdkUpdate
totrue
, which isfalse
by default, to update the component when anSDK_UPDATE
event is emitted. This is useful when you want to avoid unnecessary re-renders of your components. -
They return an object containing the SDK status properties. These properties are described in the 'Subscribe to events and changes' section and enable conditional rendering of components based on the SDK status, eliminating the need to access the Split context or use the client's
ready
promise or event listeners. For example, you can show a loading spinner until the SDK is ready, and use thetreatments
result to render the variants of your app once the SDK is ready.
The usage of the new hooks is shown below:
const { client, isReady, isReadyFromCache, hasTimedout, lastUpdate } = useSplitClient({ splitKey: userId, updateOnSdkUpdate: true });
const { treatments, isReady, isReadyFromCache, hasTimedout, lastUpdate } = useSplitTreatments({ names: ['feature-flag-1'], updateOnSdkTimedout: false });
const { manager, isReady, isReadyFromCache, hasTimedout, lastUpdate } = useSplitManager();
To migrate your existing code, replace:
const client = useClient(optionalSplitKey, optionalTrafficType, optionalAttributes);
const treatments = useTreatments(featureFlagNames, optionalAttributes, optionalSplitKey);
const manager = useManager();
with:
const { client } = useSplitClient({ splitKey: optionalSplitKey, trafficType: optionalTrafficType, attributes: optionalAttributes });
const { treatments } = useSplitTreatments({ names: featureFlagNames, attributes: optionalAttributes, splitKey: optionalSplitKey });
const { manager } = useSplitManager();
and use the status properties to conditionally render your components. For example, use the following code:
const MyComponent = ({ userId }) => {
const { treatments, isReady } = useSplitTreatments({ names: [FEATURE_X], splitKey: userId })
return isReady ?
treatments[FEATURE_X].treatment === 'on' ?
<FeatureOn /> :
<FeatureOff /> :
<LoadingPage />
}
instead of:
const MyComponent = ({ userId }) => {
const [sdkIsReady, setSdkIsReady] = useState(false);
const client = useClient(userId);
const treatments = useTreatments([FEATURE_X], undefined, userId);
useEffect(() => {
if (client) client.ready().then(() => setSdkIsReady(true));
}, [client]);
return isReady ?
treatments[FEATURE_X].treatment === 'on' ?
<FeatureOn /> :
<FeatureOff /> :
<LoadingPage />
}