Skip to content

Commit e8e0d2a

Browse files
Refactor context value to use undefined rather than null for factory and client properties
1 parent 6d65bf0 commit e8e0d2a

File tree

7 files changed

+49
-58
lines changed

7 files changed

+49
-58
lines changed

src/SplitFactoryProvider.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -28,11 +28,11 @@ export function SplitFactoryProvider(props: ISplitFactoryProviderProps) {
2828
config = undefined;
2929
}
3030

31-
const [configFactory, setConfigFactory] = React.useState<IFactoryWithClients | null>(null);
31+
const [configFactory, setConfigFactory] = React.useState<IFactoryWithClients>();
3232
const factory = React.useMemo(() => {
33-
return propFactory || (configFactory && config === configFactory.config ? configFactory : null);
33+
return propFactory || (configFactory && config === configFactory.config ? configFactory : undefined);
3434
}, [config, propFactory, configFactory]);
35-
const client = factory ? getSplitClient(factory) : null;
35+
const client = factory ? getSplitClient(factory) : undefined;
3636

3737
// Effect to initialize and destroy the factory
3838
React.useEffect(() => {

src/__tests__/SplitClient.test.tsx

Lines changed: 20 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ import { ISplitClientChildProps } from '../types';
1414
import { SplitFactoryProvider } from '../SplitFactoryProvider';
1515
import { SplitClient } from '../SplitClient';
1616
import { SplitContext } from '../SplitContext';
17-
import { testAttributesBinding, TestComponentProps } from './testUtils/utils';
17+
import { INITIAL_CONTEXT, testAttributesBinding, TestComponentProps } from './testUtils/utils';
1818
import { IClientWithContext } from '../utils';
1919
import { EXCEPTION_NO_SFP } from '../constants';
2020

@@ -24,13 +24,8 @@ describe('SplitClient', () => {
2424
render(
2525
<SplitFactoryProvider config={sdkBrowser} >
2626
<SplitClient splitKey='user1' >
27-
{({ isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => {
28-
expect(isReady).toBe(false);
29-
expect(isReadyFromCache).toBe(false);
30-
expect(hasTimedout).toBe(false);
31-
expect(isTimedout).toBe(false);
32-
expect(isDestroyed).toBe(false);
33-
expect(lastUpdate).toBe(0);
27+
{(childProps: ISplitClientChildProps) => {
28+
expect(childProps).toEqual(INITIAL_CONTEXT);
3429

3530
return null;
3631
}}
@@ -50,14 +45,15 @@ describe('SplitClient', () => {
5045
<SplitFactoryProvider factory={outerFactory} >
5146
{/* Equivalent to <SplitClient splitKey={undefined} > */}
5247
<SplitClient splitKey={sdkBrowser.core.key} >
53-
{({ client, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitClientChildProps) => {
54-
expect(client).toBe(outerFactory.client());
55-
expect(isReady).toBe(true);
56-
expect(isReadyFromCache).toBe(true);
57-
expect(hasTimedout).toBe(false);
58-
expect(isTimedout).toBe(false);
59-
expect(isDestroyed).toBe(false);
60-
expect(lastUpdate).toBe((outerFactory.client() as IClientWithContext).__getStatus().lastUpdate);
48+
{(childProps: ISplitClientChildProps) => {
49+
expect(childProps).toEqual({
50+
...INITIAL_CONTEXT,
51+
factory : outerFactory,
52+
client: outerFactory.client(),
53+
isReady: true,
54+
isReadyFromCache: true,
55+
lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate
56+
});
6157

6258
return null;
6359
}}
@@ -162,7 +158,7 @@ describe('SplitClient', () => {
162158
expect(renderTimes).toBe(3);
163159
});
164160

165-
test('rerender child only on SDK_READY event, as default behaviour.', async () => {
161+
test('rerender child only on SDK_READY event, as default behavior.', async () => {
166162
const outerFactory = SplitFactory(sdkBrowser);
167163
(outerFactory as any).client().__emitter__.emit(Event.SDK_READY);
168164
(outerFactory.manager().names as jest.Mock).mockReturnValue(['split1']);
@@ -228,18 +224,19 @@ describe('SplitClient', () => {
228224
expect(count).toEqual(2);
229225
});
230226

231-
test('renders a passed JSX.Element with a new SplitContext value.', (done) => {
227+
test('renders a passed JSX.Element with a new SplitContext value.', () => {
232228
const outerFactory = SplitFactory(sdkBrowser);
233229

234230
const Component = () => {
235231
return (
236232
<SplitContext.Consumer>
237233
{(value) => {
238-
expect(value.client).toBe(outerFactory.client('user2'));
239-
expect(value.isReady).toBe(false);
240-
expect(value.isTimedout).toBe(false);
241-
expect(value.lastUpdate).toBe(0);
242-
done();
234+
expect(value).toEqual({
235+
...INITIAL_CONTEXT,
236+
factory: outerFactory,
237+
client: outerFactory.client('user2'),
238+
});
239+
243240
return null;
244241
}}
245242
</SplitContext.Consumer>

src/__tests__/SplitFactoryProvider.test.tsx

Lines changed: 16 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -24,15 +24,8 @@ describe('SplitFactoryProvider', () => {
2424
test('passes no-ready props to the child if initialized with a config.', () => {
2525
render(
2626
<SplitFactoryProvider config={sdkBrowser} >
27-
{({ factory, client, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitFactoryProviderChildProps) => {
28-
expect(factory).toBe(null);
29-
expect(client).toBe(null);
30-
expect(isReady).toBe(false);
31-
expect(isReadyFromCache).toBe(false);
32-
expect(hasTimedout).toBe(false);
33-
expect(isTimedout).toBe(false);
34-
expect(isDestroyed).toBe(false);
35-
expect(lastUpdate).toBe(0);
27+
{(childProps: ISplitFactoryProviderChildProps) => {
28+
expect(childProps).toEqual(INITIAL_CONTEXT);
3629
return null;
3730
}}
3831
</SplitFactoryProvider>
@@ -48,15 +41,16 @@ describe('SplitFactoryProvider', () => {
4841

4942
render(
5043
<SplitFactoryProvider factory={outerFactory} >
51-
{({ factory, isReady, isReadyFromCache, hasTimedout, isTimedout, isDestroyed, lastUpdate }: ISplitFactoryProviderChildProps) => {
52-
expect(factory).toBe(outerFactory);
53-
expect(isReady).toBe(true);
54-
expect(isReadyFromCache).toBe(true);
55-
expect(hasTimedout).toBe(false);
56-
expect(isTimedout).toBe(false);
57-
expect(isDestroyed).toBe(false);
58-
expect(lastUpdate).toBe((outerFactory.client() as IClientWithContext).__getStatus().lastUpdate);
59-
expect((factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version);
44+
{(childProps: ISplitFactoryProviderChildProps) => {
45+
expect(childProps).toEqual({
46+
...INITIAL_CONTEXT,
47+
factory: outerFactory,
48+
client: outerFactory.client(),
49+
isReady: true,
50+
isReadyFromCache: true,
51+
lastUpdate: (outerFactory.client() as IClientWithContext).__getStatus().lastUpdate
52+
});
53+
expect((childProps.factory as SplitIO.IBrowserSDK).settings.version).toBe(outerFactory.settings.version);
6054
return null;
6155
}}
6256
</SplitFactoryProvider>
@@ -229,7 +223,7 @@ describe('SplitFactoryProvider', () => {
229223
expect(renderTimes).toBe(3);
230224
});
231225

232-
test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behaviour (config prop)', async () => {
226+
test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behavior (config prop)', async () => {
233227
let renderTimes = 0;
234228
let previousLastUpdate = -1;
235229

@@ -263,7 +257,7 @@ describe('SplitFactoryProvider', () => {
263257
expect(renderTimes).toBe(2);
264258
});
265259

266-
test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behaviour (factory prop)', async () => {
260+
test('rerenders child only on SDK_READY and SDK_READY_FROM_CACHE event, as default behavior (factory prop)', async () => {
267261
const outerFactory = SplitFactory(sdkBrowser);
268262
let renderTimes = 0;
269263
let previousLastUpdate = -1;
@@ -350,14 +344,14 @@ describe('SplitFactoryProvider', () => {
350344
case 5:
351345
expect(isReady).toBe(false);
352346
expect(hasTimedout).toBe(false);
353-
expect(factory).toBe(null);
347+
expect(factory).toBe(undefined);
354348
return null;
355349
case 3:
356350
case 4:
357351
case 6:
358352
expect(isReady).toBe(true);
359353
expect(hasTimedout).toBe(true);
360-
expect(factory).not.toBe(null);
354+
expect(factory).not.toBe(undefined);
361355
createdFactories.add(factory!);
362356
clientDestroySpies.push(jest.spyOn(factory!.client(), 'destroy'));
363357
return (

src/__tests__/testUtils/utils.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -117,8 +117,8 @@ export function testAttributesBinding(Component: React.FunctionComponent<TestCom
117117
}
118118

119119
export const INITIAL_CONTEXT: ISplitContextValues = {
120-
client: null,
121-
factory: null,
120+
client: undefined,
121+
factory: undefined,
122122
isReady: false,
123123
isReadyFromCache: false,
124124
isTimedout: false,

src/types.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -50,7 +50,7 @@ export interface ISplitContextValues extends ISplitStatus {
5050
*
5151
* NOTE: This property is not recommended for direct use, as better alternatives are available.
5252
*/
53-
factory: SplitIO.IBrowserSDK | null;
53+
factory?: SplitIO.IBrowserSDK;
5454

5555
/**
5656
* Split client instance.
@@ -59,7 +59,7 @@ export interface ISplitContextValues extends ISplitStatus {
5959
*
6060
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#2-instantiate-the-sdk-and-create-a-new-split-client}
6161
*/
62-
client: SplitIO.IBrowserClient | null;
62+
client?: SplitIO.IBrowserClient;
6363
}
6464

6565
/**

src/useSplitManager.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@ import { ISplitContextValues } from './types';
55
* 'useSplitManager' is a hook that returns an Split Context object with the Manager instance from the Split factory.
66
* It uses the 'useContext' hook to access the factory at Split context, which is updated by the SplitFactoryProvider component.
77
*
8-
* @returns An object containing the Split context and the Split Manager instance, which is null if used outside the scope of SplitFactoryProvider or factory is not ready.
8+
* @returns An object containing the Split context and the Split Manager instance, which is undefined if factory is not ready.
99
*
1010
* @example
1111
* ```js
@@ -14,11 +14,11 @@ import { ISplitContextValues } from './types';
1414
*
1515
* @see {@link https://help.split.io/hc/en-us/articles/360020448791-JavaScript-SDK#manager}
1616
*/
17-
export function useSplitManager(): ISplitContextValues & { manager: SplitIO.IManager | null } {
17+
export function useSplitManager(): ISplitContextValues & { manager?: SplitIO.IManager } {
1818
// Update options are not supported, because updates can be controlled at the SplitFactoryProvider component.
1919
const context = useSplitContext();
2020
return {
2121
...context,
22-
manager: context.factory ? context.factory.manager() : null
22+
manager: context.factory ? context.factory.manager() : undefined
2323
};
2424
}

src/utils.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -64,7 +64,7 @@ export function destroySplitFactory(factory: IFactoryWithClients): Promise<void>
6464

6565
// Util used to get client status.
6666
// It might be removed in the future, if the JS SDK extends its public API with a `getStatus` method
67-
export function getStatus(client: SplitIO.IBrowserClient | null): ISplitStatus {
67+
export function getStatus(client?: SplitIO.IBrowserClient): ISplitStatus {
6868
const status = client && (client as IClientWithContext).__getStatus();
6969

7070
return {
@@ -80,7 +80,7 @@ export function getStatus(client: SplitIO.IBrowserClient | null): ISplitStatus {
8080
/**
8181
* Manage client attributes binding
8282
*/
83-
export function initAttributes(client: SplitIO.IBrowserClient | null, attributes?: SplitIO.Attributes) {
83+
export function initAttributes(client?: SplitIO.IBrowserClient, attributes?: SplitIO.Attributes) {
8484
if (client && attributes) client.setAttributes(attributes);
8585
}
8686

@@ -174,7 +174,7 @@ function argsAreEqual(newArgs: any[], lastArgs: any[]): boolean {
174174
shallowEqual(newArgs[5], lastArgs[5]); // flagSets
175175
}
176176

177-
function evaluateFeatureFlags(client: SplitIO.IBrowserClient | null, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) {
177+
function evaluateFeatureFlags(client: SplitIO.IBrowserClient | undefined, _lastUpdate: number, names?: SplitIO.SplitNames, attributes?: SplitIO.Attributes, _clientAttributes?: SplitIO.Attributes, flagSets?: string[]) {
178178
if (names && flagSets) console.log(WARN_NAMES_AND_FLAGSETS);
179179

180180
return client && (client as IClientWithContext).__getStatus().isOperational && (names || flagSets) ?

0 commit comments

Comments
 (0)