1
1
'use client'
2
- import React , { use , useRef } from 'react'
2
+ import React , { Suspense , use , useEffect , useRef } from 'react'
3
3
import { DevCycleClient , initializeDevCycle } from '@devcycle/js-client-sdk'
4
4
import { invalidateConfig } from '../../common/invalidateConfig'
5
5
import { DevCycleNextOptions , DevCycleServerData } from '../../common/types'
@@ -17,16 +17,54 @@ export type DevCycleClientContext = {
17
17
18
18
type DevCycleClientsideProviderProps = {
19
19
context : DevCycleClientContext
20
- promiseResolved : boolean
21
20
children : React . ReactNode
22
21
}
23
22
24
23
const isServer = typeof window === 'undefined'
25
24
25
+ /**
26
+ * keep the clientside instance of the SDK up-to-date with new data coming from the server during realtime updates
27
+ * @param serverDataPromise
28
+ * @param client
29
+ * @param enableStreaming
30
+ * @constructor
31
+ */
32
+ const SynchronizeClientData = ( {
33
+ serverDataPromise,
34
+ client,
35
+ enableStreaming,
36
+ } : {
37
+ serverDataPromise : Promise < DevCycleServerData >
38
+ client : DevCycleClient
39
+ enableStreaming : boolean
40
+ } ) => {
41
+ const serverData = use ( serverDataPromise )
42
+ const dataRef = useRef < DevCycleServerData | null > (
43
+ // when streaming is disabled, we run synchronization on the initial server data in the
44
+ // InternalDevCycleClientsideProvider component so we don't need to do it again immediately.
45
+ // In streaming mode we want to synchronize on that initial server data since we aren't doing it above
46
+ // Therefore set this ref to the initial server data so the below check won't run when not in streaming mode
47
+ ! enableStreaming ? serverData : null ,
48
+ )
49
+ const clientRef = useRef < DevCycleClient > ( client )
50
+
51
+ if ( dataRef . current !== serverData || clientRef . current !== client ) {
52
+ dataRef . current = serverData
53
+ clientRef . current = client
54
+ // do this in a timeout to avoid setting React state in components that are subscribed to variables as a
55
+ // side effect of the current render, since this causes errors. Instead schedule the update to occur after
56
+ // render completes
57
+ setTimeout ( ( ) =>
58
+ client . synchronizeBootstrapData ( serverData . config , serverData . user ) ,
59
+ )
60
+ }
61
+
62
+ return null
63
+ }
64
+
26
65
export const InternalDevCycleClientsideProvider = ( {
27
66
context,
28
67
children,
29
- promiseResolved,
30
68
} : DevCycleClientsideProviderProps ) : React . ReactElement => {
31
69
const clientRef = useRef < DevCycleClient > ( )
32
70
const router = useRouter ( )
@@ -56,14 +94,6 @@ export const InternalDevCycleClientsideProvider = ({
56
94
}
57
95
}
58
96
59
- let resolvedServerData = serverData
60
-
61
- if ( ! serverData && promiseResolved ) {
62
- // here the provider is being told from above that this promise has already resolved, so we can safely "use"
63
- // it without blocking rendering, even in streaming mode
64
- resolvedServerData = use ( serverDataPromise )
65
- }
66
-
67
97
if ( ! clientRef . current ) {
68
98
clientRef . current = initializeDevCycle ( clientSDKKey , {
69
99
...context . options ,
@@ -81,7 +111,8 @@ export const InternalDevCycleClientsideProvider = ({
81
111
} ,
82
112
} )
83
113
84
- if ( resolvedServerData || ! enableStreaming ) {
114
+ if ( ! enableStreaming ) {
115
+ const resolvedServerData = use ( serverDataPromise )
85
116
// we expect that either the promise has resolved and we got the server data that way, or we weren't in
86
117
// streaming mode and so the promise was awaited at a higher level and passed in here as serverData
87
118
if ( ! resolvedServerData ) {
@@ -94,17 +125,6 @@ export const InternalDevCycleClientsideProvider = ({
94
125
resolvedServerData . user ,
95
126
resolvedServerData . userAgent ,
96
127
)
97
- } else {
98
- // if the promise isnt resolved yet, schedule it to synchronize when it is
99
- // we check the above condition first to make sure we can use the resolved data on the first render pass
100
- // since the `.then` here is only evaluated after this render pass is finished
101
- serverDataPromise . then ( ( serverData ) => {
102
- clientRef . current ! . synchronizeBootstrapData (
103
- serverData . config ,
104
- serverData . user ,
105
- serverData . userAgent ,
106
- )
107
- } )
108
128
}
109
129
}
110
130
@@ -117,6 +137,13 @@ export const InternalDevCycleClientsideProvider = ({
117
137
serverDataPromise,
118
138
} }
119
139
>
140
+ < Suspense fallback = { null } >
141
+ < SynchronizeClientData
142
+ serverDataPromise = { serverDataPromise }
143
+ client = { clientRef . current }
144
+ enableStreaming = { enableStreaming }
145
+ />
146
+ </ Suspense >
120
147
{ children }
121
148
</ DevCycleProviderContext . Provider >
122
149
)
0 commit comments