@livestore/react 0.3.1 → 0.3.2-dev.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/LiveStoreProvider.d.ts +5 -6
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +8 -7
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/LiveStoreProvider.test.js +3 -2
- package/dist/LiveStoreProvider.test.js.map +1 -1
- package/dist/experimental/components/LiveList.js +1 -1
- package/dist/useClientDocument.js +1 -1
- package/dist/useClientDocument.js.map +1 -1
- package/dist/useClientDocument.test.js +6 -1
- package/dist/useClientDocument.test.js.map +1 -1
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js +4 -1
- package/dist/useQuery.js.map +1 -1
- package/dist/useQuery.test.d.ts +1 -0
- package/dist/useQuery.test.d.ts.map +1 -1
- package/dist/useQuery.test.js +2 -2
- package/dist/useQuery.test.js.map +1 -1
- package/dist/useRcResource.d.ts.map +1 -1
- package/dist/useRcResource.js +2 -6
- package/dist/useRcResource.js.map +1 -1
- package/dist/useStore.js +1 -1
- package/dist/useStore.js.map +1 -1
- package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
- package/dist/utils/useStateRefWithReactiveInput.js +1 -1
- package/dist/utils/useStateRefWithReactiveInput.js.map +1 -1
- package/package.json +8 -8
- package/src/LiveStoreProvider.test.tsx +4 -2
- package/src/LiveStoreProvider.tsx +22 -23
- package/src/__snapshots__/useClientDocument.test.tsx.snap +2 -2
- package/src/experimental/components/LiveList.tsx +1 -1
- package/src/useClientDocument.test.tsx +6 -2
- package/src/useClientDocument.ts +1 -1
- package/src/useQuery.test.tsx +2 -2
- package/src/useQuery.ts +4 -1
- package/src/useRcResource.ts +2 -6
- package/src/useStore.ts +1 -1
- package/src/utils/useStateRefWithReactiveInput.ts +6 -9
@@ -3,10 +3,10 @@ import { provideOtel, UnexpectedError } from '@livestore/common'
|
|
3
3
|
import type { LiveStoreSchema } from '@livestore/common/schema'
|
4
4
|
import type {
|
5
5
|
CreateStoreOptions,
|
6
|
-
LiveStoreContext as StoreContext_,
|
7
6
|
OtelOptions,
|
8
7
|
ShutdownDeferred,
|
9
8
|
Store,
|
9
|
+
LiveStoreContext as StoreContext_,
|
10
10
|
} from '@livestore/livestore'
|
11
11
|
import { createStore, makeShutdownDeferred, StoreInterrupted } from '@livestore/livestore'
|
12
12
|
import { errorToString, IS_REACT_NATIVE, LS_DEV } from '@livestore/utils'
|
@@ -24,7 +24,6 @@ import {
|
|
24
24
|
TaskTracing,
|
25
25
|
} from '@livestore/utils/effect'
|
26
26
|
import type * as otel from '@opentelemetry/api'
|
27
|
-
import type { ReactElement, ReactNode } from 'react'
|
28
27
|
import React from 'react'
|
29
28
|
|
30
29
|
import { LiveStoreContext } from './LiveStoreContext.js'
|
@@ -47,9 +46,9 @@ export interface LiveStoreProviderProps {
|
|
47
46
|
ctx: { migrationsReport: MigrationsReport; parentSpan: otel.Span },
|
48
47
|
) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>
|
49
48
|
otelOptions?: Partial<OtelOptions>
|
50
|
-
renderLoading?: (status: BootStatus) =>
|
51
|
-
renderError?: (error: UnexpectedError | unknown) =>
|
52
|
-
renderShutdown?: (cause: IntentionalShutdownCause | StoreInterrupted) =>
|
49
|
+
renderLoading?: (status: BootStatus) => React.ReactNode
|
50
|
+
renderError?: (error: UnexpectedError | unknown) => React.ReactNode
|
51
|
+
renderShutdown?: (cause: IntentionalShutdownCause | StoreInterrupted) => React.ReactNode
|
53
52
|
adapter: Adapter
|
54
53
|
/**
|
55
54
|
* In order for LiveStore to apply multiple events in a single render,
|
@@ -85,7 +84,7 @@ export interface LiveStoreProviderProps {
|
|
85
84
|
}
|
86
85
|
|
87
86
|
const defaultRenderError = (error: UnexpectedError | unknown) =>
|
88
|
-
IS_REACT_NATIVE ?
|
87
|
+
IS_REACT_NATIVE ? null : Schema.is(UnexpectedError)(error) ? error.toString() : errorToString(error)
|
89
88
|
|
90
89
|
const defaultRenderShutdown = (cause: IntentionalShutdownCause | StoreInterrupted) => {
|
91
90
|
const reason =
|
@@ -101,11 +100,11 @@ const defaultRenderShutdown = (cause: IntentionalShutdownCause | StoreInterrupte
|
|
101
100
|
? 'manual shutdown'
|
102
101
|
: 'unknown reason'
|
103
102
|
|
104
|
-
return IS_REACT_NATIVE ?
|
103
|
+
return IS_REACT_NATIVE ? null : <>LiveStore Shutdown due to {reason}</>
|
105
104
|
}
|
106
105
|
|
107
106
|
const defaultRenderLoading = (status: BootStatus) =>
|
108
|
-
IS_REACT_NATIVE ?
|
107
|
+
IS_REACT_NATIVE ? null : <>LiveStore is loading ({status.stage})...</>
|
109
108
|
|
110
109
|
export const LiveStoreProvider = ({
|
111
110
|
renderLoading = defaultRenderLoading,
|
@@ -123,7 +122,7 @@ export const LiveStoreProvider = ({
|
|
123
122
|
confirmUnsavedChanges = true,
|
124
123
|
syncPayload,
|
125
124
|
debug,
|
126
|
-
}: LiveStoreProviderProps & { children?: ReactNode }): React.
|
125
|
+
}: LiveStoreProviderProps & { children?: React.ReactNode }): React.ReactNode => {
|
127
126
|
const storeCtx = useCreateStore({
|
128
127
|
storeId,
|
129
128
|
schema,
|
@@ -152,7 +151,7 @@ export const LiveStoreProvider = ({
|
|
152
151
|
|
153
152
|
globalThis.__debugLiveStore ??= {}
|
154
153
|
if (Object.keys(globalThis.__debugLiveStore).length === 0) {
|
155
|
-
globalThis.__debugLiveStore
|
154
|
+
globalThis.__debugLiveStore._ = storeCtx.store
|
156
155
|
}
|
157
156
|
globalThis.__debugLiveStore[debug?.instanceId ?? storeId] = storeCtx.store
|
158
157
|
|
@@ -211,19 +210,18 @@ const useCreateStore = ({
|
|
211
210
|
debugInstanceId,
|
212
211
|
})
|
213
212
|
|
214
|
-
const interrupt = (
|
215
|
-
componentScope: Scope.CloseableScope,
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
)
|
213
|
+
const interrupt = React.useCallback(
|
214
|
+
(componentScope: Scope.CloseableScope, shutdownDeferred: ShutdownDeferred, error: StoreInterrupted) =>
|
215
|
+
Effect.gen(function* () {
|
216
|
+
// console.log('[@livestore/livestore/react] interupting', error)
|
217
|
+
yield* Scope.close(componentScope, Exit.fail(error))
|
218
|
+
yield* Deferred.fail(shutdownDeferred, error)
|
219
|
+
}).pipe(
|
220
|
+
Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] interupting', cause)),
|
221
|
+
Effect.runFork,
|
222
|
+
),
|
223
|
+
[],
|
224
|
+
)
|
227
225
|
|
228
226
|
const inputPropChanges = {
|
229
227
|
schema: inputPropsCacheRef.current.schema !== schema,
|
@@ -400,6 +398,7 @@ const useCreateStore = ({
|
|
400
398
|
confirmUnsavedChanges,
|
401
399
|
syncPayload,
|
402
400
|
debugInstanceId,
|
401
|
+
interrupt,
|
403
402
|
])
|
404
403
|
|
405
404
|
return ctxValueRef.current.value
|
@@ -37,7 +37,7 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
37
37
|
{
|
38
38
|
"_name": "client-session-sync-processor:pull",
|
39
39
|
"attributes": {
|
40
|
-
"code.stacktrace": "
|
40
|
+
"code.stacktrace": "<STACKTRACE>",
|
41
41
|
"span.label": "⚠︎ Interrupted",
|
42
42
|
"status.interrupted": true,
|
43
43
|
},
|
@@ -263,7 +263,7 @@ exports[`useClientDocument > otel > should update the data based on component ke
|
|
263
263
|
{
|
264
264
|
"_name": "client-session-sync-processor:pull",
|
265
265
|
"attributes": {
|
266
|
-
"code.stacktrace": "
|
266
|
+
"code.stacktrace": "<STACKTRACE>",
|
267
267
|
"span.label": "⚠︎ Interrupted",
|
268
268
|
"status.interrupted": true,
|
269
269
|
},
|
@@ -81,7 +81,7 @@ const ItemWrapperMemo = React.memo(
|
|
81
81
|
ItemWrapper,
|
82
82
|
(prev, next) =>
|
83
83
|
prev.itemKey === next.itemKey &&
|
84
|
-
prev.renderItem ===
|
84
|
+
prev.renderItem === next.renderItem &&
|
85
85
|
prev.opts.index === next.opts.index &&
|
86
86
|
prev.opts.isInitialListRender === next.opts.isInitialListRender,
|
87
87
|
) as typeof ItemWrapper
|
@@ -1,3 +1,5 @@
|
|
1
|
+
/** biome-ignore-all lint/a11y/useValidAriaRole: not needed for testing */
|
2
|
+
/** biome-ignore-all lint/a11y/noStaticElementInteractions: not needed for testing */
|
1
3
|
import * as LiveStore from '@livestore/livestore'
|
2
4
|
import { getSimplifiedRootSpan } from '@livestore/livestore/internal/testing-utils'
|
3
5
|
import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
|
@@ -5,7 +7,7 @@ import { Vitest } from '@livestore/utils-dev/node-vitest'
|
|
5
7
|
import * as otel from '@opentelemetry/api'
|
6
8
|
import { BasicTracerProvider, InMemorySpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base'
|
7
9
|
import * as ReactTesting from '@testing-library/react'
|
8
|
-
import React from 'react'
|
10
|
+
import type React from 'react'
|
9
11
|
import { beforeEach, expect, it } from 'vitest'
|
10
12
|
|
11
13
|
import { events, makeTodoMvcReact, tables } from './__tests__/fixture.js'
|
@@ -282,7 +284,9 @@ Vitest.describe('useClientDocument', () => {
|
|
282
284
|
|
283
285
|
const mapAttributes = (attributes: otel.Attributes) => {
|
284
286
|
return ReadonlyRecord.map(attributes, (val, key) => {
|
285
|
-
if (key === '
|
287
|
+
if (key === 'code.stacktrace') {
|
288
|
+
return '<STACKTRACE>'
|
289
|
+
} else if (key === 'firstStackInfo') {
|
286
290
|
const stackInfo = JSON.parse(val as string) as LiveStore.StackInfo
|
287
291
|
// stackInfo.frames.shift() // Removes `renderHook.wrapper` from the stack
|
288
292
|
stackInfo.frames.forEach((_) => {
|
package/src/useClientDocument.ts
CHANGED
@@ -98,7 +98,7 @@ export const useClientDocument: {
|
|
98
98
|
|
99
99
|
const store =
|
100
100
|
storeArg?.store ??
|
101
|
-
//
|
101
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: store is stable
|
102
102
|
React.useContext(LiveStoreContext)?.store ??
|
103
103
|
shouldNeverHappen(`No store provided to useClientDocument`)
|
104
104
|
|
package/src/useQuery.test.tsx
CHANGED
@@ -1,7 +1,8 @@
|
|
1
|
+
/** biome-ignore-all lint/a11y: test */
|
1
2
|
import '@livestore/utils-dev/node-vitest-polyfill'
|
2
3
|
|
3
|
-
import { queryDb, signal } from '@livestore/livestore'
|
4
4
|
import * as LiveStore from '@livestore/livestore'
|
5
|
+
import { queryDb, signal } from '@livestore/livestore'
|
5
6
|
import { RG } from '@livestore/livestore/internal/testing-utils'
|
6
7
|
import { Effect, Schema } from '@livestore/utils/effect'
|
7
8
|
import { Vitest } from '@livestore/utils-dev/node-vitest'
|
@@ -161,7 +162,6 @@ Vitest.describe.each([{ strictMode: true }, { strictMode: false }] as const)(
|
|
161
162
|
const ListItem: React.FC<{ data: ReadonlyArray<number>; index: number }> = ({ data: ids, index }) => {
|
162
163
|
const id = ids[index]!
|
163
164
|
const res = store.useQuery(LiveStore.computed(() => id, { label: `ListItem.${id}`, deps: id }))
|
164
|
-
// biome-ignore lint/a11y/useSemanticElements: <explanation>
|
165
165
|
return <div role="listitem">{res}</div>
|
166
166
|
}
|
167
167
|
|
package/src/useQuery.ts
CHANGED
@@ -43,7 +43,10 @@ export const useQueryRef = <TQuery extends LiveQueryDef.Any>(
|
|
43
43
|
queryRcRef: LiveQueries.RcRef<LiveQuery<LiveQueries.GetResult<TQuery>>>
|
44
44
|
} => {
|
45
45
|
const store =
|
46
|
-
options?.store ??
|
46
|
+
options?.store ??
|
47
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: store is stable
|
48
|
+
React.useContext(LiveStoreContext)?.store ??
|
49
|
+
shouldNeverHappen(`No store provided to useQuery`)
|
47
50
|
|
48
51
|
// It's important to use all "aspects" of a store instance here, otherwise we get unexpected cache mappings
|
49
52
|
const rcRefKey = `${store.storeId}_${store.clientId}_${store.sessionId}_${queryDef.hash}`
|
package/src/useRcResource.ts
CHANGED
@@ -80,7 +80,7 @@ export const useRcResource = <T>(
|
|
80
80
|
const keyRef = React.useRef<string | undefined>(undefined)
|
81
81
|
const didDisposeInMemo = React.useRef(false)
|
82
82
|
|
83
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
83
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: Dependency is deliberately limited to `key` to avoid unintended re-creations.
|
84
84
|
const resource = React.useMemo(() => {
|
85
85
|
// console.debug('useMemo', key)
|
86
86
|
if (didDisposeInMemo.current) {
|
@@ -125,11 +125,9 @@ export const useRcResource = <T>(
|
|
125
125
|
const resource = create()
|
126
126
|
cache.set(key, { _tag: 'active', rc: 1, resource })
|
127
127
|
return resource
|
128
|
-
// Dependency is deliberately limited to `key` to avoid unintended re-creations.
|
129
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
130
128
|
}, [key])
|
131
129
|
|
132
|
-
// biome-ignore lint/correctness/useExhaustiveDependencies:
|
130
|
+
// biome-ignore lint/correctness/useExhaustiveDependencies: We assume the `dispose` function is stable and won't change across renders
|
133
131
|
React.useEffect(() => {
|
134
132
|
return () => {
|
135
133
|
if (didDisposeInMemo.current) {
|
@@ -152,8 +150,6 @@ export const useRcResource = <T>(
|
|
152
150
|
cache.delete(key)
|
153
151
|
}
|
154
152
|
}
|
155
|
-
// We assume the `dispose` function is stable and won't change across renders
|
156
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
157
153
|
}, [key])
|
158
154
|
|
159
155
|
keyRef.current = key
|
package/src/useStore.ts
CHANGED
@@ -21,7 +21,7 @@ export const useStore = (options?: { store?: Store }): { store: Store & ReactApi
|
|
21
21
|
return { store: withReactApi(options.store) }
|
22
22
|
}
|
23
23
|
|
24
|
-
//
|
24
|
+
// biome-ignore lint/correctness/useHookAtTopLevel: store is stable
|
25
25
|
const storeContext = React.useContext(LiveStoreContext)
|
26
26
|
|
27
27
|
if (storeContext === undefined) {
|
@@ -25,15 +25,12 @@ export const useStateRefWithReactiveInput = <T>(
|
|
25
25
|
stateRef.current = inputState
|
26
26
|
}
|
27
27
|
|
28
|
-
const setStateAndRerender = React.useCallback(
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
},
|
35
|
-
[rerender],
|
36
|
-
)
|
28
|
+
const setStateAndRerender = React.useCallback((newState: ((prev: T) => T) | T) => {
|
29
|
+
// @ts-expect-error https://github.com/microsoft/TypeScript/issues/37663
|
30
|
+
const val = typeof newState === 'function' ? newState(stateRef.current) : newState
|
31
|
+
stateRef.current = val
|
32
|
+
rerender((c) => c + 1)
|
33
|
+
}, [])
|
37
34
|
|
38
35
|
return [stateRef, setStateAndRerender]
|
39
36
|
}
|