Skip to content

Commit 628b185

Browse files
Make factory and client context properties available in first render when a config prop is provided
1 parent 4688ff0 commit 628b185

10 files changed

+71
-96
lines changed

src/SplitFactoryProvider.tsx

Lines changed: 11 additions & 53 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,7 @@ import React from 'react';
33
import { SplitClient } from './SplitClient';
44
import { ISplitFactoryProviderProps } from './types';
55
import { WARN_SF_CONFIG_AND_FACTORY } from './constants';
6-
import { getSplitFactory, destroySplitFactory, IFactoryWithClients, getSplitClient, getStatus } from './utils';
7-
import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient';
6+
import { getSplitFactory, destroySplitFactory, getSplitClient, getStatus } from './utils';
87
import { SplitContext } from './SplitContext';
98

109
/**
@@ -18,70 +17,29 @@ import { SplitContext } from './SplitContext';
1817
* @see {@link https://help.split.io/hc/en-us/articles/360038825091-React-SDK#2-instantiate-the-sdk-and-create-a-new-split-client}
1918
*/
2019
export function SplitFactoryProvider(props: ISplitFactoryProviderProps) {
21-
let {
22-
config, factory: propFactory,
23-
updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate
24-
} = { ...DEFAULT_UPDATE_OPTIONS, ...props };
20+
const { config, factory: propFactory } = props;
2521

26-
if (config && propFactory) {
27-
console.log(WARN_SF_CONFIG_AND_FACTORY);
28-
config = undefined;
29-
}
30-
31-
const [configFactory, setConfigFactory] = React.useState<IFactoryWithClients>();
3222
const factory = React.useMemo(() => {
33-
return propFactory || (configFactory && config === configFactory.config ? configFactory : undefined);
34-
}, [config, propFactory, configFactory]);
23+
return propFactory || (config ? getSplitFactory(config) : undefined);
24+
}, [config, propFactory]);
3525
const client = factory ? getSplitClient(factory) : undefined;
3626

3727
// Effect to initialize and destroy the factory
3828
React.useEffect(() => {
29+
if (propFactory) {
30+
if (config) console.log(WARN_SF_CONFIG_AND_FACTORY);
31+
return;
32+
}
33+
3934
if (config) {
4035
const factory = getSplitFactory(config);
36+
factory.init && factory.init();
4137

4238
return () => {
4339
destroySplitFactory(factory);
4440
}
4541
}
46-
}, [config]);
47-
48-
// Effect to subscribe/unsubscribe to events
49-
React.useEffect(() => {
50-
const factory = config && getSplitFactory(config);
51-
if (factory) {
52-
const client = getSplitClient(factory);
53-
const status = getStatus(client);
54-
55-
// Unsubscribe from events and update state when first event is emitted
56-
const update = () => { // eslint-disable-next-line no-use-before-define
57-
unsubscribe();
58-
setConfigFactory(factory);
59-
}
60-
61-
const unsubscribe = () => {
62-
client.off(client.Event.SDK_READY, update);
63-
client.off(client.Event.SDK_READY_FROM_CACHE, update);
64-
client.off(client.Event.SDK_READY_TIMED_OUT, update);
65-
client.off(client.Event.SDK_UPDATE, update);
66-
}
67-
68-
if (updateOnSdkReady) {
69-
if (status.isReady) update();
70-
else client.once(client.Event.SDK_READY, update);
71-
}
72-
if (updateOnSdkReadyFromCache) {
73-
if (status.isReadyFromCache) update();
74-
else client.once(client.Event.SDK_READY_FROM_CACHE, update);
75-
}
76-
if (updateOnSdkTimedout) {
77-
if (status.hasTimedout) update();
78-
else client.once(client.Event.SDK_READY_TIMED_OUT, update);
79-
}
80-
if (updateOnSdkUpdate) client.on(client.Event.SDK_UPDATE, update);
81-
82-
return unsubscribe;
83-
}
84-
}, [config, updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate]);
42+
}, [config, propFactory]);
8543

8644
return (
8745
<SplitContext.Provider value={{

src/__tests__/SplitClient.test.tsx

Lines changed: 9 additions & 5 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 } from './testUtils/mockSplitFactory';
5+
import { mockSdk, Event, getLastInstance } from './testUtils/mockSplitFactory';
66
jest.mock('@splitsoftware/splitio/client', () => {
77
return { SplitFactory: mockSdk() };
88
});
@@ -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 { INITIAL_CONTEXT, testAttributesBinding, TestComponentProps } from './testUtils/utils';
17+
import { INITIAL_STATUS, testAttributesBinding, TestComponentProps } from './testUtils/utils';
1818
import { IClientWithContext } from '../utils';
1919
import { EXCEPTION_NO_SFP } from '../constants';
2020

@@ -25,7 +25,11 @@ describe('SplitClient', () => {
2525
<SplitFactoryProvider config={sdkBrowser} >
2626
<SplitClient splitKey='user1' >
2727
{(childProps: ISplitClientChildProps) => {
28-
expect(childProps).toEqual(INITIAL_CONTEXT);
28+
expect(childProps).toEqual({
29+
...INITIAL_STATUS,
30+
factory: getLastInstance(SplitFactory),
31+
client: getLastInstance(SplitFactory).client('user1'),
32+
});
2933

3034
return null;
3135
}}
@@ -47,7 +51,7 @@ describe('SplitClient', () => {
4751
<SplitClient splitKey={sdkBrowser.core.key} >
4852
{(childProps: ISplitClientChildProps) => {
4953
expect(childProps).toEqual({
50-
...INITIAL_CONTEXT,
54+
...INITIAL_STATUS,
5155
factory : outerFactory,
5256
client: outerFactory.client(),
5357
isReady: true,
@@ -232,7 +236,7 @@ describe('SplitClient', () => {
232236
<SplitContext.Consumer>
233237
{(value) => {
234238
expect(value).toEqual({
235-
...INITIAL_CONTEXT,
239+
...INITIAL_STATUS,
236240
factory: outerFactory,
237241
client: outerFactory.client('user2'),
238242
});

src/__tests__/SplitContext.test.tsx

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,7 @@ import React from 'react';
22
import { render } from '@testing-library/react';
33
import { SplitContext } from '../SplitContext';
44
import { SplitFactoryProvider } from '../SplitFactoryProvider';
5-
import { INITIAL_CONTEXT } from './testUtils/utils';
5+
import { INITIAL_STATUS } from './testUtils/utils';
66

77
/**
88
* Test default SplitContext value
@@ -23,7 +23,11 @@ test('SplitContext.Consumer shows value when wrapped in a SplitFactoryProvider',
2323
<SplitFactoryProvider >
2424
<SplitContext.Consumer>
2525
{(value) => {
26-
expect(value).toEqual(INITIAL_CONTEXT);
26+
expect(value).toEqual({
27+
...INITIAL_STATUS,
28+
factory: undefined,
29+
client: undefined
30+
});
2731
return null;
2832
}}
2933
</SplitContext.Consumer>

src/__tests__/SplitFactoryProvider.test.tsx

Lines changed: 22 additions & 14 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 } from './testUtils/mockSplitFactory';
5+
import { mockSdk, Event, getLastInstance } from './testUtils/mockSplitFactory';
66
jest.mock('@splitsoftware/splitio/client', () => {
77
return { SplitFactory: mockSdk() };
88
});
@@ -17,15 +17,19 @@ import { SplitClient } from '../SplitClient';
1717
import { SplitContext } from '../SplitContext';
1818
import { __factories, IClientWithContext } from '../utils';
1919
import { WARN_SF_CONFIG_AND_FACTORY } from '../constants';
20-
import { INITIAL_CONTEXT } from './testUtils/utils';
20+
import { INITIAL_STATUS } from './testUtils/utils';
2121

2222
describe('SplitFactoryProvider', () => {
2323

2424
test('passes no-ready props to the child if initialized with a config.', () => {
2525
render(
2626
<SplitFactoryProvider config={sdkBrowser} >
2727
{(childProps: ISplitFactoryProviderChildProps) => {
28-
expect(childProps).toEqual(INITIAL_CONTEXT);
28+
expect(childProps).toEqual({
29+
...INITIAL_STATUS,
30+
factory: getLastInstance(SplitFactory),
31+
client: getLastInstance(SplitFactory).client(),
32+
});
2933
return null;
3034
}}
3135
</SplitFactoryProvider>
@@ -43,7 +47,7 @@ describe('SplitFactoryProvider', () => {
4347
<SplitFactoryProvider factory={outerFactory} >
4448
{(childProps: ISplitFactoryProviderChildProps) => {
4549
expect(childProps).toEqual({
46-
...INITIAL_CONTEXT,
50+
...INITIAL_STATUS,
4751
factory: outerFactory,
4852
client: outerFactory.client(),
4953
isReady: true,
@@ -84,7 +88,7 @@ describe('SplitFactoryProvider', () => {
8488
default:
8589
fail('Child must not be rerendered');
8690
} // eslint-disable-next-line no-use-before-define
87-
if (factory) expect(factory).toBe(innerFactory);
91+
expect(factory).toBe(innerFactory || getLastInstance(SplitFactory));
8892
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
8993
renderTimes++;
9094
previousLastUpdate = lastUpdate;
@@ -93,7 +97,7 @@ describe('SplitFactoryProvider', () => {
9397
</SplitFactoryProvider>
9498
);
9599

96-
const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value;
100+
const innerFactory = getLastInstance(SplitFactory);
97101
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT));
98102
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_FROM_CACHE));
99103
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY));
@@ -168,7 +172,7 @@ describe('SplitFactoryProvider', () => {
168172
default:
169173
fail('Child must not be rerendered');
170174
} // eslint-disable-next-line no-use-before-define
171-
if (factory) expect(factory).toBe(innerFactory);
175+
expect(factory).toBe(innerFactory || getLastInstance(SplitFactory));
172176
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
173177
renderTimes++;
174178
previousLastUpdate = lastUpdate;
@@ -177,7 +181,7 @@ describe('SplitFactoryProvider', () => {
177181
</SplitFactoryProvider>
178182
);
179183

180-
const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value;
184+
const innerFactory = getLastInstance(SplitFactory);
181185
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT));
182186
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY));
183187
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE));
@@ -241,7 +245,7 @@ describe('SplitFactoryProvider', () => {
241245
default:
242246
fail('Child must not be rerendered');
243247
} // eslint-disable-next-line no-use-before-define
244-
if (factory) expect(factory).toBe(innerFactory);
248+
expect(factory).toBe(innerFactory || getLastInstance(SplitFactory));
245249
expect(lastUpdate).toBeGreaterThan(previousLastUpdate);
246250
renderTimes++;
247251
previousLastUpdate = lastUpdate;
@@ -250,7 +254,7 @@ describe('SplitFactoryProvider', () => {
250254
</SplitFactoryProvider>
251255
);
252256

253-
const innerFactory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value;
257+
const innerFactory = getLastInstance(SplitFactory);
254258
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY_TIMED_OUT));
255259
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_READY));
256260
act(() => (innerFactory as any).client().__emitter__.emit(Event.SDK_UPDATE));
@@ -296,7 +300,11 @@ describe('SplitFactoryProvider', () => {
296300
return (
297301
<SplitContext.Consumer>
298302
{(value) => {
299-
expect(value).toEqual(INITIAL_CONTEXT);
303+
expect(value).toEqual({
304+
...INITIAL_STATUS,
305+
factory: getLastInstance(SplitFactory),
306+
client: getLastInstance(SplitFactory).client(),
307+
});
300308
done();
301309
return null;
302310
}}
@@ -344,14 +352,14 @@ describe('SplitFactoryProvider', () => {
344352
case 5:
345353
expect(isReady).toBe(false);
346354
expect(hasTimedout).toBe(false);
347-
expect(factory).toBe(undefined);
355+
expect(factory).toBe(getLastInstance(SplitFactory));
348356
return null;
349357
case 3:
350358
case 4:
351359
case 6:
352360
expect(isReady).toBe(true);
353361
expect(hasTimedout).toBe(true);
354-
expect(factory).not.toBe(undefined);
362+
expect(factory).toBe(getLastInstance(SplitFactory));
355363
createdFactories.add(factory!);
356364
clientDestroySpies.push(jest.spyOn(factory!.client(), 'destroy'));
357365
return (
@@ -368,7 +376,7 @@ describe('SplitFactoryProvider', () => {
368376
};
369377

370378
const emitSdkEvents = () => {
371-
const factory = (SplitFactory as jest.Mock).mock.results.slice(-1)[0].value;
379+
const factory = getLastInstance(SplitFactory);
372380
factory.client().__emitter__.emit(Event.SDK_READY_TIMED_OUT)
373381
factory.client().__emitter__.emit(Event.SDK_READY)
374382
};

src/__tests__/testUtils/mockSplitFactory.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -164,3 +164,7 @@ export function mockSdk() {
164164
});
165165

166166
}
167+
168+
export function getLastInstance(SplitFactoryMock: any) {
169+
return SplitFactoryMock.mock.results.slice(-1)[0].value;
170+
}

src/__tests__/testUtils/utils.tsx

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import React from 'react';
22
import { render } from '@testing-library/react';
3-
import { ISplitContextValues } from '../../types';
3+
import { ISplitStatus } from '../../types';
44
const { SplitFactory: originalSplitFactory } = jest.requireActual('@splitsoftware/splitio/client');
55

66
export interface TestComponentProps {
@@ -116,9 +116,7 @@ export function testAttributesBinding(Component: React.FunctionComponent<TestCom
116116
wrapper.rerender(<Component splitKey={undefined} attributesFactory={undefined} attributesClient={undefined} testSwitch={attributesBindingSwitch} factory={factory} />);
117117
}
118118

119-
export const INITIAL_CONTEXT: ISplitContextValues = {
120-
client: undefined,
121-
factory: undefined,
119+
export const INITIAL_STATUS: ISplitStatus = {
122120
isReady: false,
123121
isReadyFromCache: false,
124122
isTimedout: false,

src/__tests__/useSplitManager.test.tsx

Lines changed: 2 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@ import { getStatus } from '../utils';
1414
import { SplitFactoryProvider } from '../SplitFactoryProvider';
1515
import { useSplitManager } from '../useSplitManager';
1616
import { EXCEPTION_NO_SFP } from '../constants';
17+
import { INITIAL_STATUS } from './testUtils/utils';
1718

1819
describe('useSplitManager', () => {
1920

@@ -33,12 +34,7 @@ describe('useSplitManager', () => {
3334
manager: outerFactory.manager(),
3435
client: outerFactory.client(),
3536
factory: outerFactory,
36-
hasTimedout: false,
37-
isDestroyed: false,
38-
isReady: false,
39-
isReadyFromCache: false,
40-
isTimedout: false,
41-
lastUpdate: 0,
37+
...INITIAL_STATUS,
4238
});
4339

4440
act(() => (outerFactory.client() as any).__emitter__.emit(Event.SDK_READY));

0 commit comments

Comments
 (0)