@livestore/react 0.3.0-dev.9 → 0.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/.tsbuildinfo +1 -1
- package/dist/LiveStoreContext.d.ts +10 -4
- package/dist/LiveStoreContext.d.ts.map +1 -1
- package/dist/LiveStoreContext.js +1 -11
- package/dist/LiveStoreContext.js.map +1 -1
- package/dist/LiveStoreProvider.d.ts +29 -12
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +84 -55
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/LiveStoreProvider.test.js +80 -29
- package/dist/LiveStoreProvider.test.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +122 -556
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +71 -30
- package/dist/__tests__/fixture.js.map +1 -1
- package/dist/experimental/components/LiveList.d.ts +2 -2
- package/dist/experimental/components/LiveList.d.ts.map +1 -1
- package/dist/experimental/components/LiveList.js +10 -6
- package/dist/experimental/components/LiveList.js.map +1 -1
- package/dist/mod.d.ts +4 -5
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +4 -5
- package/dist/mod.js.map +1 -1
- package/dist/useClientDocument.d.ts +61 -0
- package/dist/useClientDocument.d.ts.map +1 -0
- package/dist/useClientDocument.js +79 -0
- package/dist/useClientDocument.js.map +1 -0
- package/dist/useClientDocument.test.d.ts +2 -0
- package/dist/useClientDocument.test.d.ts.map +1 -0
- package/dist/useClientDocument.test.js +175 -0
- package/dist/useClientDocument.test.js.map +1 -0
- package/dist/useQuery.d.ts +25 -3
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js +67 -47
- package/dist/useQuery.js.map +1 -1
- package/dist/useQuery.test.d.ts +1 -1
- package/dist/useQuery.test.d.ts.map +1 -1
- package/dist/useQuery.test.js +86 -24
- package/dist/useQuery.test.js.map +1 -1
- package/dist/useRcResource.d.ts +76 -0
- package/dist/useRcResource.d.ts.map +1 -0
- package/dist/useRcResource.js +152 -0
- package/dist/useRcResource.js.map +1 -0
- package/dist/useRcResource.test.d.ts +2 -0
- package/dist/useRcResource.test.d.ts.map +1 -0
- package/dist/useRcResource.test.js +122 -0
- package/dist/useRcResource.test.js.map +1 -0
- package/dist/useStore.d.ts +9 -0
- package/dist/useStore.d.ts.map +1 -0
- package/dist/useStore.js +28 -0
- package/dist/useStore.js.map +1 -0
- package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
- package/package.json +19 -13
- package/src/LiveStoreContext.ts +11 -16
- package/src/LiveStoreProvider.test.tsx +176 -37
- package/src/LiveStoreProvider.tsx +156 -81
- package/src/__snapshots__/useClientDocument.test.tsx.snap +613 -0
- package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
- package/src/__tests__/fixture.tsx +74 -47
- package/src/experimental/components/LiveList.tsx +10 -7
- package/src/mod.ts +5 -6
- package/src/useClientDocument.test.tsx +306 -0
- package/src/useClientDocument.ts +157 -0
- package/src/useQuery.test.tsx +182 -71
- package/src/useQuery.ts +95 -58
- package/src/useRcResource.test.tsx +167 -0
- package/src/useRcResource.ts +182 -0
- package/src/useStore.ts +36 -0
- package/dist/useAtom.d.ts +0 -5
- package/dist/useAtom.d.ts.map +0 -1
- package/dist/useAtom.js +0 -38
- package/dist/useAtom.js.map +0 -1
- package/dist/useRow.d.ts +0 -50
- package/dist/useRow.d.ts.map +0 -1
- package/dist/useRow.js +0 -93
- package/dist/useRow.js.map +0 -1
- package/dist/useRow.test.d.ts +0 -2
- package/dist/useRow.test.d.ts.map +0 -1
- package/dist/useRow.test.js +0 -202
- package/dist/useRow.test.js.map +0 -1
- package/dist/useScopedQuery.d.ts +0 -33
- package/dist/useScopedQuery.d.ts.map +0 -1
- package/dist/useScopedQuery.js +0 -87
- package/dist/useScopedQuery.js.map +0 -1
- package/dist/useScopedQuery.test.d.ts +0 -2
- package/dist/useScopedQuery.test.d.ts.map +0 -1
- package/dist/useScopedQuery.test.js +0 -60
- package/dist/useScopedQuery.test.js.map +0 -1
- package/src/__snapshots__/useRow.test.tsx.snap +0 -360
- package/src/useAtom.ts +0 -52
- package/src/useRow.test.tsx +0 -344
- package/src/useRow.ts +0 -188
- package/src/useScopedQuery.test.tsx +0 -96
- package/src/useScopedQuery.ts +0 -143
- package/tsconfig.json +0 -20
- package/vitest.config.js +0 -17
@@ -1,26 +1,35 @@
|
|
1
|
-
import type { Adapter, BootStatus, IntentionalShutdownCause } from '@livestore/common'
|
1
|
+
import type { Adapter, BootStatus, IntentionalShutdownCause, MigrationsReport } from '@livestore/common'
|
2
2
|
import { provideOtel, UnexpectedError } from '@livestore/common'
|
3
3
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
4
4
|
import type {
|
5
|
-
BaseGraphQLContext,
|
6
5
|
CreateStoreOptions,
|
7
|
-
GraphQLOptions,
|
8
6
|
LiveStoreContext as StoreContext_,
|
9
7
|
OtelOptions,
|
10
8
|
ShutdownDeferred,
|
11
9
|
Store,
|
12
10
|
} from '@livestore/livestore'
|
13
|
-
import { createStore,
|
14
|
-
import { errorToString, LS_DEV } from '@livestore/utils'
|
11
|
+
import { createStore, makeShutdownDeferred, StoreInterrupted } from '@livestore/livestore'
|
12
|
+
import { errorToString, IS_REACT_NATIVE, LS_DEV } from '@livestore/utils'
|
15
13
|
import type { OtelTracer } from '@livestore/utils/effect'
|
16
|
-
import {
|
14
|
+
import {
|
15
|
+
Cause,
|
16
|
+
Deferred,
|
17
|
+
Effect,
|
18
|
+
Exit,
|
19
|
+
identity,
|
20
|
+
Logger,
|
21
|
+
LogLevel,
|
22
|
+
Schema,
|
23
|
+
Scope,
|
24
|
+
TaskTracing,
|
25
|
+
} from '@livestore/utils/effect'
|
17
26
|
import type * as otel from '@opentelemetry/api'
|
18
27
|
import type { ReactElement, ReactNode } from 'react'
|
19
28
|
import React from 'react'
|
20
29
|
|
21
30
|
import { LiveStoreContext } from './LiveStoreContext.js'
|
22
31
|
|
23
|
-
interface LiveStoreProviderProps
|
32
|
+
export interface LiveStoreProviderProps {
|
24
33
|
schema: LiveStoreSchema
|
25
34
|
/**
|
26
35
|
* The `storeId` can be used to isolate multiple stores from each other.
|
@@ -34,17 +43,16 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
34
43
|
*/
|
35
44
|
storeId?: string
|
36
45
|
boot?: (
|
37
|
-
store: Store<
|
38
|
-
parentSpan: otel.Span,
|
46
|
+
store: Store<LiveStoreSchema>,
|
47
|
+
ctx: { migrationsReport: MigrationsReport; parentSpan: otel.Span },
|
39
48
|
) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>
|
40
|
-
graphQLOptions?: GraphQLOptions<GraphQLContext>
|
41
49
|
otelOptions?: Partial<OtelOptions>
|
42
|
-
renderLoading
|
50
|
+
renderLoading?: (status: BootStatus) => ReactElement
|
43
51
|
renderError?: (error: UnexpectedError | unknown) => ReactElement
|
44
|
-
renderShutdown?: (cause: IntentionalShutdownCause |
|
52
|
+
renderShutdown?: (cause: IntentionalShutdownCause | StoreInterrupted) => ReactElement
|
45
53
|
adapter: Adapter
|
46
54
|
/**
|
47
|
-
* In order for LiveStore to apply multiple
|
55
|
+
* In order for LiveStore to apply multiple events in a single render,
|
48
56
|
* you need to pass the `batchUpdates` function from either `react-dom` or `react-native`.
|
49
57
|
*
|
50
58
|
* ```ts
|
@@ -58,31 +66,51 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
58
66
|
batchUpdates: (run: () => void) => void
|
59
67
|
disableDevtools?: boolean
|
60
68
|
signal?: AbortSignal
|
69
|
+
/**
|
70
|
+
* Currently only used in the web adapter:
|
71
|
+
* If true, registers a beforeunload event listener to confirm unsaved changes.
|
72
|
+
*
|
73
|
+
* @default true
|
74
|
+
*/
|
75
|
+
confirmUnsavedChanges?: boolean
|
76
|
+
/**
|
77
|
+
* Payload that will be passed to the sync backend when connecting
|
78
|
+
*
|
79
|
+
* @default undefined
|
80
|
+
*/
|
81
|
+
syncPayload?: Schema.JsonValue
|
82
|
+
debug?: {
|
83
|
+
instanceId?: string
|
84
|
+
}
|
61
85
|
}
|
62
86
|
|
63
|
-
const defaultRenderError = (error: UnexpectedError | unknown) =>
|
64
|
-
<>{Schema.is(UnexpectedError)(error) ? error.toString() : errorToString(error)}</>
|
65
|
-
|
66
|
-
const defaultRenderShutdown = (cause: IntentionalShutdownCause |
|
87
|
+
const defaultRenderError = (error: UnexpectedError | unknown) =>
|
88
|
+
IS_REACT_NATIVE ? <></> : <>{Schema.is(UnexpectedError)(error) ? error.toString() : errorToString(error)}</>
|
89
|
+
|
90
|
+
const defaultRenderShutdown = (cause: IntentionalShutdownCause | StoreInterrupted) => {
|
67
91
|
const reason =
|
68
|
-
cause._tag === 'LiveStore.
|
69
|
-
?
|
92
|
+
cause._tag === 'LiveStore.StoreInterrupted'
|
93
|
+
? `interrupted due to: ${cause.reason}`
|
70
94
|
: cause.reason === 'devtools-import'
|
71
95
|
? 'devtools import'
|
72
96
|
: cause.reason === 'devtools-reset'
|
73
97
|
? 'devtools reset'
|
74
|
-
: cause.reason === '
|
75
|
-
? '
|
76
|
-
:
|
98
|
+
: cause.reason === 'adapter-reset'
|
99
|
+
? 'adapter reset'
|
100
|
+
: cause.reason === 'manual'
|
101
|
+
? 'manual shutdown'
|
102
|
+
: 'unknown reason'
|
77
103
|
|
78
|
-
return <>LiveStore Shutdown due to {reason}</>
|
104
|
+
return IS_REACT_NATIVE ? <></> : <>LiveStore Shutdown due to {reason}</>
|
79
105
|
}
|
80
106
|
|
81
|
-
|
82
|
-
|
107
|
+
const defaultRenderLoading = (status: BootStatus) =>
|
108
|
+
IS_REACT_NATIVE ? <></> : <>LiveStore is loading ({status.stage})...</>
|
109
|
+
|
110
|
+
export const LiveStoreProvider = ({
|
111
|
+
renderLoading = defaultRenderLoading,
|
83
112
|
renderError = defaultRenderError,
|
84
113
|
renderShutdown = defaultRenderShutdown,
|
85
|
-
graphQLOptions,
|
86
114
|
otelOptions,
|
87
115
|
children,
|
88
116
|
schema,
|
@@ -92,17 +120,22 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
92
120
|
batchUpdates,
|
93
121
|
disableDevtools,
|
94
122
|
signal,
|
95
|
-
|
123
|
+
confirmUnsavedChanges = true,
|
124
|
+
syncPayload,
|
125
|
+
debug,
|
126
|
+
}: LiveStoreProviderProps & { children?: ReactNode }): React.ReactElement => {
|
96
127
|
const storeCtx = useCreateStore({
|
97
128
|
storeId,
|
98
129
|
schema,
|
99
|
-
graphQLOptions,
|
100
130
|
otelOptions,
|
101
131
|
boot,
|
102
132
|
adapter,
|
103
133
|
batchUpdates,
|
104
134
|
disableDevtools,
|
105
135
|
signal,
|
136
|
+
confirmUnsavedChanges,
|
137
|
+
syncPayload,
|
138
|
+
debug,
|
106
139
|
})
|
107
140
|
|
108
141
|
if (storeCtx.stage === 'error') {
|
@@ -121,35 +154,26 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
121
154
|
if (Object.keys(globalThis.__debugLiveStore).length === 0) {
|
122
155
|
globalThis.__debugLiveStore['_'] = storeCtx.store
|
123
156
|
}
|
124
|
-
globalThis.__debugLiveStore[storeId] = storeCtx.store
|
157
|
+
globalThis.__debugLiveStore[debug?.instanceId ?? storeId] = storeCtx.store
|
125
158
|
|
126
|
-
return <LiveStoreContext.Provider value={storeCtx}>{children}</LiveStoreContext.Provider>
|
159
|
+
return <LiveStoreContext.Provider value={storeCtx as TODO}>{children}</LiveStoreContext.Provider>
|
127
160
|
}
|
128
161
|
|
129
|
-
|
130
|
-
const semaphoreMap = new Map<SchemaKey, Effect.Semaphore>()
|
131
|
-
|
132
|
-
const withSemaphore = (storeId: SchemaKey) => {
|
133
|
-
let semaphore = semaphoreMap.get(storeId)
|
134
|
-
if (!semaphore) {
|
135
|
-
semaphore = Effect.makeSemaphore(1).pipe(Effect.runSync)
|
136
|
-
semaphoreMap.set(storeId, semaphore)
|
137
|
-
}
|
138
|
-
return semaphore.withPermits(1)
|
139
|
-
}
|
140
|
-
|
141
|
-
const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
162
|
+
const useCreateStore = ({
|
142
163
|
schema,
|
143
164
|
storeId,
|
144
|
-
graphQLOptions,
|
145
165
|
otelOptions,
|
146
166
|
boot,
|
147
167
|
adapter,
|
148
168
|
batchUpdates,
|
149
169
|
disableDevtools,
|
150
|
-
reactivityGraph,
|
151
170
|
signal,
|
152
|
-
|
171
|
+
context,
|
172
|
+
params,
|
173
|
+
confirmUnsavedChanges,
|
174
|
+
syncPayload,
|
175
|
+
debug,
|
176
|
+
}: CreateStoreOptions<LiveStoreSchema> & {
|
153
177
|
signal?: AbortSignal
|
154
178
|
otelOptions?: Partial<OtelOptions>
|
155
179
|
}) => {
|
@@ -158,32 +182,39 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
158
182
|
value: StoreContext_ | BootStatus
|
159
183
|
componentScope: Scope.CloseableScope | undefined
|
160
184
|
shutdownDeferred: ShutdownDeferred | undefined
|
185
|
+
/** Used to wait for the previous shutdown deferred to fully complete before creating a new one */
|
186
|
+
previousShutdownDeferred: ShutdownDeferred | undefined
|
161
187
|
counter: number
|
162
188
|
}>({
|
163
189
|
value: { stage: 'loading' },
|
164
190
|
componentScope: undefined,
|
165
191
|
shutdownDeferred: undefined,
|
192
|
+
previousShutdownDeferred: undefined,
|
166
193
|
counter: 0,
|
167
194
|
})
|
195
|
+
const debugInstanceId = debug?.instanceId
|
168
196
|
|
169
197
|
// console.debug(`useCreateStore (${ctxValueRef.current.counter})`, ctxValueRef.current.value.stage)
|
170
198
|
|
171
199
|
const inputPropsCacheRef = React.useRef({
|
172
200
|
schema,
|
173
|
-
graphQLOptions,
|
174
201
|
otelOptions,
|
175
202
|
boot,
|
176
203
|
adapter,
|
177
204
|
batchUpdates,
|
178
205
|
disableDevtools,
|
179
|
-
reactivityGraph,
|
180
206
|
signal,
|
207
|
+
context,
|
208
|
+
params,
|
209
|
+
confirmUnsavedChanges,
|
210
|
+
syncPayload,
|
211
|
+
debugInstanceId,
|
181
212
|
})
|
182
213
|
|
183
214
|
const interrupt = (
|
184
215
|
componentScope: Scope.CloseableScope,
|
185
216
|
shutdownDeferred: ShutdownDeferred,
|
186
|
-
error:
|
217
|
+
error: StoreInterrupted,
|
187
218
|
) =>
|
188
219
|
Effect.gen(function* () {
|
189
220
|
// console.log('[@livestore/livestore/react] interupting', error)
|
@@ -194,30 +225,58 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
194
225
|
Effect.runFork,
|
195
226
|
)
|
196
227
|
|
228
|
+
const inputPropChanges = {
|
229
|
+
schema: inputPropsCacheRef.current.schema !== schema,
|
230
|
+
otelOptions: inputPropsCacheRef.current.otelOptions !== otelOptions,
|
231
|
+
boot: inputPropsCacheRef.current.boot !== boot,
|
232
|
+
adapter: inputPropsCacheRef.current.adapter !== adapter,
|
233
|
+
batchUpdates: inputPropsCacheRef.current.batchUpdates !== batchUpdates,
|
234
|
+
disableDevtools: inputPropsCacheRef.current.disableDevtools !== disableDevtools,
|
235
|
+
signal: inputPropsCacheRef.current.signal !== signal,
|
236
|
+
context: inputPropsCacheRef.current.context !== context,
|
237
|
+
params: inputPropsCacheRef.current.params !== params,
|
238
|
+
confirmUnsavedChanges: inputPropsCacheRef.current.confirmUnsavedChanges !== confirmUnsavedChanges,
|
239
|
+
syncPayload: inputPropsCacheRef.current.syncPayload !== syncPayload,
|
240
|
+
debugInstanceId: inputPropsCacheRef.current.debugInstanceId !== debugInstanceId,
|
241
|
+
}
|
242
|
+
|
197
243
|
if (
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
244
|
+
inputPropChanges.schema ||
|
245
|
+
inputPropChanges.otelOptions ||
|
246
|
+
inputPropChanges.boot ||
|
247
|
+
inputPropChanges.adapter ||
|
248
|
+
inputPropChanges.batchUpdates ||
|
249
|
+
inputPropChanges.disableDevtools ||
|
250
|
+
inputPropChanges.signal ||
|
251
|
+
inputPropChanges.context ||
|
252
|
+
inputPropChanges.params ||
|
253
|
+
inputPropChanges.confirmUnsavedChanges ||
|
254
|
+
inputPropChanges.syncPayload
|
207
255
|
) {
|
208
256
|
inputPropsCacheRef.current = {
|
209
257
|
schema,
|
210
|
-
graphQLOptions,
|
211
258
|
otelOptions,
|
212
259
|
boot,
|
213
260
|
adapter,
|
214
261
|
batchUpdates,
|
215
262
|
disableDevtools,
|
216
|
-
reactivityGraph,
|
217
263
|
signal,
|
264
|
+
context,
|
265
|
+
params,
|
266
|
+
confirmUnsavedChanges,
|
267
|
+
syncPayload,
|
268
|
+
debugInstanceId,
|
218
269
|
}
|
219
270
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
220
|
-
|
271
|
+
const changedInputProps = Object.keys(inputPropChanges).filter(
|
272
|
+
(key) => inputPropChanges[key as keyof typeof inputPropChanges],
|
273
|
+
)
|
274
|
+
|
275
|
+
interrupt(
|
276
|
+
ctxValueRef.current.componentScope,
|
277
|
+
ctxValueRef.current.shutdownDeferred,
|
278
|
+
new StoreInterrupted({ reason: `re-rendering due to changed input props: ${changedInputProps.join(', ')}` }),
|
279
|
+
)
|
221
280
|
ctxValueRef.current.componentScope = undefined
|
222
281
|
ctxValueRef.current.shutdownDeferred = undefined
|
223
282
|
}
|
@@ -225,6 +284,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
225
284
|
value: { stage: 'loading' },
|
226
285
|
componentScope: undefined,
|
227
286
|
shutdownDeferred: undefined,
|
287
|
+
previousShutdownDeferred: ctxValueRef.current.shutdownDeferred,
|
228
288
|
counter: ctxValueRef.current.counter + 1,
|
229
289
|
}
|
230
290
|
}
|
@@ -244,18 +304,24 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
244
304
|
ctxValueRef.current.shutdownDeferred !== undefined &&
|
245
305
|
ctxValueRef.current.counter === counter
|
246
306
|
) {
|
247
|
-
interrupt(
|
307
|
+
interrupt(
|
308
|
+
ctxValueRef.current.componentScope,
|
309
|
+
ctxValueRef.current.shutdownDeferred,
|
310
|
+
new StoreInterrupted({ reason: 'Aborted via provided AbortController' }),
|
311
|
+
)
|
248
312
|
ctxValueRef.current.componentScope = undefined
|
249
313
|
ctxValueRef.current.shutdownDeferred = undefined
|
250
314
|
}
|
251
315
|
})
|
252
316
|
|
253
|
-
Effect.gen(function* () {
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
317
|
+
const cancel = Effect.gen(function* () {
|
318
|
+
// Wait for the previous store to fully shutdown before creating a new one
|
319
|
+
if (ctxValueRef.current.previousShutdownDeferred) {
|
320
|
+
yield* Deferred.await(ctxValueRef.current.previousShutdownDeferred)
|
321
|
+
}
|
322
|
+
|
323
|
+
const componentScope = yield* Scope.make().pipe(Effect.acquireRelease(Scope.close))
|
324
|
+
const shutdownDeferred = yield* makeShutdownDeferred
|
259
325
|
|
260
326
|
ctxValueRef.current.componentScope = componentScope
|
261
327
|
ctxValueRef.current.shutdownDeferred = shutdownDeferred
|
@@ -264,39 +330,39 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
264
330
|
const store = yield* createStore({
|
265
331
|
schema,
|
266
332
|
storeId,
|
267
|
-
graphQLOptions,
|
268
333
|
boot,
|
269
334
|
adapter,
|
270
|
-
reactivityGraph,
|
271
335
|
batchUpdates,
|
272
336
|
disableDevtools,
|
273
337
|
shutdownDeferred,
|
338
|
+
context,
|
339
|
+
params,
|
340
|
+
confirmUnsavedChanges,
|
341
|
+
syncPayload,
|
274
342
|
onBootStatus: (status) => {
|
275
343
|
if (ctxValueRef.current.value.stage === 'running' || ctxValueRef.current.value.stage === 'error') return
|
344
|
+
// NOTE sometimes when status come in in rapid succession, only the last value will be rendered by React
|
276
345
|
setContextValue(status)
|
277
346
|
},
|
347
|
+
debug: { instanceId: debugInstanceId },
|
278
348
|
}).pipe(Effect.tapErrorCause((cause) => Deferred.failCause(shutdownDeferred, cause)))
|
279
349
|
|
280
350
|
setContextValue({ stage: 'running', store })
|
281
351
|
}).pipe(Scope.extend(componentScope), Effect.forkIn(componentScope))
|
282
352
|
|
283
|
-
const shutdownContext = (cause: IntentionalShutdownCause |
|
353
|
+
const shutdownContext = (cause: IntentionalShutdownCause | StoreInterrupted) =>
|
284
354
|
Effect.sync(() => setContextValue({ stage: 'shutdown', cause }))
|
285
355
|
|
286
356
|
yield* Deferred.await(shutdownDeferred).pipe(
|
287
|
-
Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', cause)),
|
357
|
+
Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', Cause.pretty(cause))),
|
288
358
|
Effect.catchTag('LiveStore.IntentionalShutdownCause', (cause) => shutdownContext(cause)),
|
289
|
-
Effect.catchTag('LiveStore.
|
359
|
+
Effect.catchTag('LiveStore.StoreInterrupted', (cause) => shutdownContext(cause)),
|
290
360
|
Effect.tapError((error) => Effect.sync(() => setContextValue({ stage: 'error', error }))),
|
291
361
|
Effect.tapDefect((defect) => Effect.sync(() => setContextValue({ stage: 'error', error: defect }))),
|
292
362
|
Effect.exit,
|
293
363
|
)
|
294
364
|
}).pipe(
|
295
365
|
Effect.scoped,
|
296
|
-
// NOTE we're running the code above in a semaphore to make sure a previous store is always fully
|
297
|
-
// shutdown before a new one is created - especially when shutdown logic is async. You can't trust `React.useEffect`.
|
298
|
-
// Thank you to Mattia Manzati for this idea.
|
299
|
-
withSemaphore(storeId),
|
300
366
|
Effect.withSpan('@livestore/react:useCreateStore'),
|
301
367
|
LS_DEV ? TaskTracing.withAsyncTaggingTracing((name: string) => (console as any).createTask(name)) : identity,
|
302
368
|
provideOtel({ parentSpanContext: otelOptions?.rootSpanContext, otelTracer: otelOptions?.tracer }),
|
@@ -304,27 +370,36 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
304
370
|
Effect.annotateLogs({ thread: 'window' }),
|
305
371
|
Effect.provide(Logger.prettyWithThread('window')),
|
306
372
|
Logger.withMinimumLogLevel(LogLevel.Debug),
|
307
|
-
Effect.
|
373
|
+
Effect.runCallback,
|
308
374
|
)
|
309
375
|
|
310
376
|
return () => {
|
377
|
+
cancel()
|
378
|
+
|
311
379
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
312
|
-
interrupt(
|
380
|
+
interrupt(
|
381
|
+
ctxValueRef.current.componentScope,
|
382
|
+
ctxValueRef.current.shutdownDeferred,
|
383
|
+
new StoreInterrupted({ reason: 'unmounting component' }),
|
384
|
+
)
|
313
385
|
ctxValueRef.current.componentScope = undefined
|
314
386
|
ctxValueRef.current.shutdownDeferred = undefined
|
315
387
|
}
|
316
388
|
}
|
317
389
|
}, [
|
318
390
|
schema,
|
319
|
-
graphQLOptions,
|
320
391
|
otelOptions,
|
321
392
|
boot,
|
322
393
|
adapter,
|
323
394
|
batchUpdates,
|
324
395
|
disableDevtools,
|
325
396
|
signal,
|
326
|
-
reactivityGraph,
|
327
397
|
storeId,
|
398
|
+
context,
|
399
|
+
params,
|
400
|
+
confirmUnsavedChanges,
|
401
|
+
syncPayload,
|
402
|
+
debugInstanceId,
|
328
403
|
])
|
329
404
|
|
330
405
|
return ctxValueRef.current.value
|