@livestore/react 0.3.0-dev.18 → 0.3.0-dev.21
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/LiveStoreProvider.d.ts +11 -6
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +31 -11
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/LiveStoreProvider.test.js +1 -1
- package/dist/LiveStoreProvider.test.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +2 -2
- package/dist/useAtom.d.ts +4 -3
- package/dist/useAtom.d.ts.map +1 -1
- package/dist/useAtom.js.map +1 -1
- package/dist/useQuery.d.ts +8 -7
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js.map +1 -1
- package/package.json +12 -11
- package/src/LiveStoreProvider.test.tsx +1 -1
- package/src/LiveStoreProvider.tsx +57 -20
- package/src/useAtom.ts +8 -4
- package/src/useQuery.ts +10 -9
|
@@ -2,9 +2,7 @@ import type { Adapter, BootStatus, IntentionalShutdownCause, MigrationsReport }
|
|
|
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,
|
|
@@ -13,14 +11,25 @@ import type {
|
|
|
13
11
|
import { createStore, StoreInterrupted } from '@livestore/livestore'
|
|
14
12
|
import { errorToString, 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,10 +43,9 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
|
34
43
|
*/
|
|
35
44
|
storeId?: string
|
|
36
45
|
boot?: (
|
|
37
|
-
store: Store<
|
|
46
|
+
store: Store<LiveStoreSchema>,
|
|
38
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
50
|
renderLoading: (status: BootStatus) => ReactElement
|
|
43
51
|
renderError?: (error: UnexpectedError | unknown) => ReactElement
|
|
@@ -58,6 +66,13 @@ 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
|
|
61
76
|
}
|
|
62
77
|
|
|
63
78
|
const defaultRenderError = (error: UnexpectedError | unknown) => (
|
|
@@ -78,11 +93,10 @@ const defaultRenderShutdown = (cause: IntentionalShutdownCause | StoreInterrupte
|
|
|
78
93
|
return <>LiveStore Shutdown due to {reason}</>
|
|
79
94
|
}
|
|
80
95
|
|
|
81
|
-
export const LiveStoreProvider =
|
|
96
|
+
export const LiveStoreProvider = ({
|
|
82
97
|
renderLoading,
|
|
83
98
|
renderError = defaultRenderError,
|
|
84
99
|
renderShutdown = defaultRenderShutdown,
|
|
85
|
-
graphQLOptions,
|
|
86
100
|
otelOptions,
|
|
87
101
|
children,
|
|
88
102
|
schema,
|
|
@@ -92,17 +106,18 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
92
106
|
batchUpdates,
|
|
93
107
|
disableDevtools,
|
|
94
108
|
signal,
|
|
95
|
-
|
|
109
|
+
confirmUnsavedChanges = true,
|
|
110
|
+
}: LiveStoreProviderProps & { children?: ReactNode }): React.ReactElement => {
|
|
96
111
|
const storeCtx = useCreateStore({
|
|
97
112
|
storeId,
|
|
98
113
|
schema,
|
|
99
|
-
graphQLOptions,
|
|
100
114
|
otelOptions,
|
|
101
115
|
boot,
|
|
102
116
|
adapter,
|
|
103
117
|
batchUpdates,
|
|
104
118
|
disableDevtools,
|
|
105
119
|
signal,
|
|
120
|
+
confirmUnsavedChanges,
|
|
106
121
|
})
|
|
107
122
|
|
|
108
123
|
if (storeCtx.stage === 'error') {
|
|
@@ -138,17 +153,19 @@ const withSemaphore = (storeId: SchemaKey) => {
|
|
|
138
153
|
return semaphore.withPermits(1)
|
|
139
154
|
}
|
|
140
155
|
|
|
141
|
-
const useCreateStore =
|
|
156
|
+
const useCreateStore = ({
|
|
142
157
|
schema,
|
|
143
158
|
storeId,
|
|
144
|
-
graphQLOptions,
|
|
145
159
|
otelOptions,
|
|
146
160
|
boot,
|
|
147
161
|
adapter,
|
|
148
162
|
batchUpdates,
|
|
149
163
|
disableDevtools,
|
|
150
164
|
signal,
|
|
151
|
-
|
|
165
|
+
context,
|
|
166
|
+
params,
|
|
167
|
+
confirmUnsavedChanges,
|
|
168
|
+
}: CreateStoreOptions<LiveStoreSchema> & {
|
|
152
169
|
signal?: AbortSignal
|
|
153
170
|
otelOptions?: Partial<OtelOptions>
|
|
154
171
|
}) => {
|
|
@@ -169,13 +186,15 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
169
186
|
|
|
170
187
|
const inputPropsCacheRef = React.useRef({
|
|
171
188
|
schema,
|
|
172
|
-
graphQLOptions,
|
|
173
189
|
otelOptions,
|
|
174
190
|
boot,
|
|
175
191
|
adapter,
|
|
176
192
|
batchUpdates,
|
|
177
193
|
disableDevtools,
|
|
178
194
|
signal,
|
|
195
|
+
context,
|
|
196
|
+
params,
|
|
197
|
+
confirmUnsavedChanges,
|
|
179
198
|
})
|
|
180
199
|
|
|
181
200
|
const interrupt = (
|
|
@@ -194,23 +213,27 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
194
213
|
|
|
195
214
|
if (
|
|
196
215
|
inputPropsCacheRef.current.schema !== schema ||
|
|
197
|
-
inputPropsCacheRef.current.graphQLOptions !== graphQLOptions ||
|
|
198
216
|
inputPropsCacheRef.current.otelOptions !== otelOptions ||
|
|
199
217
|
inputPropsCacheRef.current.boot !== boot ||
|
|
200
218
|
inputPropsCacheRef.current.adapter !== adapter ||
|
|
201
219
|
inputPropsCacheRef.current.batchUpdates !== batchUpdates ||
|
|
202
220
|
inputPropsCacheRef.current.disableDevtools !== disableDevtools ||
|
|
203
|
-
inputPropsCacheRef.current.signal !== signal
|
|
221
|
+
inputPropsCacheRef.current.signal !== signal ||
|
|
222
|
+
inputPropsCacheRef.current.context !== context ||
|
|
223
|
+
inputPropsCacheRef.current.params !== params ||
|
|
224
|
+
inputPropsCacheRef.current.confirmUnsavedChanges !== confirmUnsavedChanges
|
|
204
225
|
) {
|
|
205
226
|
inputPropsCacheRef.current = {
|
|
206
227
|
schema,
|
|
207
|
-
graphQLOptions,
|
|
208
228
|
otelOptions,
|
|
209
229
|
boot,
|
|
210
230
|
adapter,
|
|
211
231
|
batchUpdates,
|
|
212
232
|
disableDevtools,
|
|
213
233
|
signal,
|
|
234
|
+
context,
|
|
235
|
+
params,
|
|
236
|
+
confirmUnsavedChanges,
|
|
214
237
|
}
|
|
215
238
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
|
216
239
|
interrupt(
|
|
@@ -268,12 +291,14 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
268
291
|
const store = yield* createStore({
|
|
269
292
|
schema,
|
|
270
293
|
storeId,
|
|
271
|
-
graphQLOptions,
|
|
272
294
|
boot,
|
|
273
295
|
adapter,
|
|
274
296
|
batchUpdates,
|
|
275
297
|
disableDevtools,
|
|
276
298
|
shutdownDeferred,
|
|
299
|
+
context,
|
|
300
|
+
params,
|
|
301
|
+
confirmUnsavedChanges,
|
|
277
302
|
onBootStatus: (status) => {
|
|
278
303
|
if (ctxValueRef.current.value.stage === 'running' || ctxValueRef.current.value.stage === 'error') return
|
|
279
304
|
// NOTE sometimes when status come in in rapid succession, only the last value will be rendered by React
|
|
@@ -288,7 +313,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
288
313
|
Effect.sync(() => setContextValue({ stage: 'shutdown', cause }))
|
|
289
314
|
|
|
290
315
|
yield* Deferred.await(shutdownDeferred).pipe(
|
|
291
|
-
Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', cause)),
|
|
316
|
+
Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', Cause.pretty(cause))),
|
|
292
317
|
Effect.catchTag('LiveStore.IntentionalShutdownCause', (cause) => shutdownContext(cause)),
|
|
293
318
|
Effect.catchTag('LiveStore.StoreInterrupted', (cause) => shutdownContext(cause)),
|
|
294
319
|
Effect.tapError((error) => Effect.sync(() => setContextValue({ stage: 'error', error }))),
|
|
@@ -322,7 +347,19 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
322
347
|
ctxValueRef.current.shutdownDeferred = undefined
|
|
323
348
|
}
|
|
324
349
|
}
|
|
325
|
-
}, [
|
|
350
|
+
}, [
|
|
351
|
+
schema,
|
|
352
|
+
otelOptions,
|
|
353
|
+
boot,
|
|
354
|
+
adapter,
|
|
355
|
+
batchUpdates,
|
|
356
|
+
disableDevtools,
|
|
357
|
+
signal,
|
|
358
|
+
storeId,
|
|
359
|
+
context,
|
|
360
|
+
params,
|
|
361
|
+
confirmUnsavedChanges,
|
|
362
|
+
])
|
|
326
363
|
|
|
327
364
|
return ctxValueRef.current.value
|
|
328
365
|
}
|
package/src/useAtom.ts
CHANGED
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import type { DerivedMutationHelperFns, QueryInfo } from '@livestore/common'
|
|
2
2
|
import type { DbSchema, SqliteDsl } from '@livestore/common/schema'
|
|
3
|
-
import type {
|
|
3
|
+
import type { Store } from '@livestore/livestore'
|
|
4
|
+
import type { LiveQueries } from '@livestore/livestore/internal'
|
|
4
5
|
import { shouldNeverHappen } from '@livestore/utils'
|
|
5
6
|
import React from 'react'
|
|
6
7
|
|
|
@@ -10,13 +11,16 @@ import type { Dispatch, SetStateAction } from './useRow.js'
|
|
|
10
11
|
|
|
11
12
|
export const useAtom = <
|
|
12
13
|
// TODO also support colJsonValue
|
|
13
|
-
TQuery extends LiveQueryDef<any, QueryInfo.Row | QueryInfo.Col>,
|
|
14
|
+
TQuery extends LiveQueries.LiveQueryDef<any, QueryInfo.Row | QueryInfo.Col>,
|
|
14
15
|
>(
|
|
15
16
|
queryDef: TQuery,
|
|
16
17
|
options?: {
|
|
17
18
|
store?: Store
|
|
18
19
|
},
|
|
19
|
-
): [
|
|
20
|
+
): [
|
|
21
|
+
value: LiveQueries.GetResult<TQuery>,
|
|
22
|
+
setValue: Dispatch<SetStateAction<Partial<LiveQueries.GetResult<TQuery>>>>,
|
|
23
|
+
] => {
|
|
20
24
|
const queryRef = useQueryRef(queryDef, { store: options?.store })
|
|
21
25
|
const query$ = queryRef.queryRcRef.value
|
|
22
26
|
|
|
@@ -28,7 +32,7 @@ export const useAtom = <
|
|
|
28
32
|
const { store } = useStore()
|
|
29
33
|
|
|
30
34
|
// TODO make API equivalent to useRow
|
|
31
|
-
const setValue = React.useMemo<Dispatch<SetStateAction<Partial<GetResult<TQuery>>>>>(
|
|
35
|
+
const setValue = React.useMemo<Dispatch<SetStateAction<Partial<LiveQueries.GetResult<TQuery>>>>>(
|
|
32
36
|
() => (newValueOrFn: any) => {
|
|
33
37
|
const newValue = typeof newValueOrFn === 'function' ? newValueOrFn(queryRef.valueRef.current) : newValueOrFn
|
|
34
38
|
const table = query$.queryInfo.table as DbSchema.TableDef &
|
package/src/useQuery.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LiveQuery, LiveQueryDef, Store } from '@livestore/livestore'
|
|
2
2
|
import { extractStackInfoFromStackTrace, stackInfoToString } from '@livestore/livestore'
|
|
3
|
+
import type { LiveQueries } from '@livestore/livestore/internal'
|
|
3
4
|
import { deepEqual, indent } from '@livestore/utils'
|
|
4
5
|
import * as otel from '@opentelemetry/api'
|
|
5
6
|
import React from 'react'
|
|
@@ -20,17 +21,17 @@ import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInp
|
|
|
20
21
|
* }
|
|
21
22
|
* ```
|
|
22
23
|
*/
|
|
23
|
-
export const useQuery = <TQuery extends
|
|
24
|
+
export const useQuery = <TQuery extends LiveQueryDef.Any>(
|
|
24
25
|
queryDef: TQuery,
|
|
25
26
|
options?: { store?: Store },
|
|
26
|
-
): GetResult<TQuery> => useQueryRef(queryDef, options).valueRef.current
|
|
27
|
+
): LiveQueries.GetResult<TQuery> => useQueryRef(queryDef, options).valueRef.current
|
|
27
28
|
|
|
28
|
-
type GetQueryInfo<TQuery extends
|
|
29
|
+
type GetQueryInfo<TQuery extends LiveQueryDef.Any> =
|
|
29
30
|
TQuery extends LiveQueryDef<infer _1, infer TQueryInfo> ? TQueryInfo : never
|
|
30
31
|
|
|
31
32
|
/**
|
|
32
33
|
*/
|
|
33
|
-
export const useQueryRef = <TQuery extends
|
|
34
|
+
export const useQueryRef = <TQuery extends LiveQueryDef.Any>(
|
|
34
35
|
queryDef: TQuery,
|
|
35
36
|
options?: {
|
|
36
37
|
store?: Store
|
|
@@ -40,8 +41,8 @@ export const useQueryRef = <TQuery extends LiveQueryDefAny>(
|
|
|
40
41
|
otelSpanName?: string
|
|
41
42
|
},
|
|
42
43
|
): {
|
|
43
|
-
valueRef: React.RefObject<GetResult<TQuery>>
|
|
44
|
-
queryRcRef: RcRef<LiveQuery<GetResult<TQuery>, GetQueryInfo<TQuery>>>
|
|
44
|
+
valueRef: React.RefObject<LiveQueries.GetResult<TQuery>>
|
|
45
|
+
queryRcRef: LiveQueries.RcRef<LiveQuery<LiveQueries.GetResult<TQuery>, GetQueryInfo<TQuery>>>
|
|
45
46
|
} => {
|
|
46
47
|
const { store } = useStore({ store: options?.store })
|
|
47
48
|
|
|
@@ -76,7 +77,7 @@ export const useQueryRef = <TQuery extends LiveQueryDefAny>(
|
|
|
76
77
|
// which takes care of disposing the queryRcRef
|
|
77
78
|
() => {},
|
|
78
79
|
)
|
|
79
|
-
const query$ = queryRcRef.value as LiveQuery<GetResult<TQuery>, GetQueryInfo<TQuery>>
|
|
80
|
+
const query$ = queryRcRef.value as LiveQuery<LiveQueries.GetResult<TQuery>, GetQueryInfo<TQuery>>
|
|
80
81
|
|
|
81
82
|
React.useDebugValue(`LiveStore:useQuery:${query$.id}:${query$.label}`)
|
|
82
83
|
// console.debug(`LiveStore:useQuery:${query$.id}:${query$.label}`)
|
|
@@ -111,7 +112,7 @@ Stack trace:
|
|
|
111
112
|
}, [otelContext, query$, stackInfo])
|
|
112
113
|
|
|
113
114
|
// We know the query has a result by the time we use it; so we can synchronously populate a default state
|
|
114
|
-
const [valueRef, setValue] = useStateRefWithReactiveInput<GetResult<TQuery>>(initialResult)
|
|
115
|
+
const [valueRef, setValue] = useStateRefWithReactiveInput<LiveQueries.GetResult<TQuery>>(initialResult)
|
|
115
116
|
|
|
116
117
|
// TODO we probably need to change the order of `useEffect` calls, so we destroy the query at the end
|
|
117
118
|
// before calling the LS `onEffect` on it
|