|
| 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 } from './utils'; |
| 7 | +import { DEFAULT_UPDATE_OPTIONS } from './useSplitClient'; |
| 8 | + |
| 9 | +/** |
| 10 | + * SplitFactoryProvider will initialize the Split SDK and its main client when it is mounted, listen for its events in order to update the Split Context, |
| 11 | + * and automatically shutdown and release resources when it is unmounted. SplitFactoryProvider must wrap other library components and functions |
| 12 | + * since they access the Split Context and its elements (factory, clients, 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 [stateFactory, setStateFactory] = React.useState(propFactory || null); |
| 31 | + const factory = propFactory || stateFactory; |
| 32 | + const client = factory ? getSplitClient(factory) : null; |
| 33 | + |
| 34 | + React.useEffect(() => { |
| 35 | + if (config) { |
| 36 | + const factory = getSplitFactory(config); |
| 37 | + const client = getSplitClient(factory); |
| 38 | + const status = getStatus(client); |
| 39 | + |
| 40 | + // Update state and unsubscribe from events when first event is emitted |
| 41 | + const update = () => { |
| 42 | + client.off(client.Event.SDK_READY, update); |
| 43 | + client.off(client.Event.SDK_READY_FROM_CACHE, update); |
| 44 | + client.off(client.Event.SDK_READY_TIMED_OUT, update); |
| 45 | + client.off(client.Event.SDK_UPDATE, update); |
| 46 | + |
| 47 | + setStateFactory(factory); |
| 48 | + } |
| 49 | + |
| 50 | + if (updateOnSdkReady) { |
| 51 | + if (status.isReady) update(); |
| 52 | + else client.once(client.Event.SDK_READY, update); |
| 53 | + } |
| 54 | + if (updateOnSdkReadyFromCache) { |
| 55 | + if (status.isReadyFromCache) update(); |
| 56 | + else client.once(client.Event.SDK_READY_FROM_CACHE, update); |
| 57 | + } |
| 58 | + if (updateOnSdkTimedout) { |
| 59 | + if (status.hasTimedout) update(); |
| 60 | + else client.once(client.Event.SDK_READY_TIMED_OUT, update); |
| 61 | + } |
| 62 | + if (updateOnSdkUpdate) client.on(client.Event.SDK_UPDATE, update); |
| 63 | + |
| 64 | + return () => { |
| 65 | + // Factory destroy unsubscribes from events |
| 66 | + destroySplitFactory(factory as IFactoryWithClients); |
| 67 | + } |
| 68 | + } |
| 69 | + }, [config, updateOnSdkReady, updateOnSdkReadyFromCache, updateOnSdkTimedout, updateOnSdkUpdate]); |
| 70 | + |
| 71 | + return ( |
| 72 | + <SplitComponent {...props} factory={factory} client={client} /> |
| 73 | + ); |
| 74 | +} |
0 commit comments