@livestore/livestore 0.0.13 → 0.0.14
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/README.md +18 -21
- package/dist/.tsbuildinfo +1 -1
- package/dist/QueryCache.d.ts +1 -1
- package/dist/QueryCache.d.ts.map +1 -1
- package/dist/QueryCache.js.map +1 -1
- package/dist/__tests__/react/fixture.d.ts +5 -4
- package/dist/__tests__/react/fixture.d.ts.map +1 -1
- package/dist/__tests__/react/fixture.js +13 -14
- package/dist/__tests__/react/fixture.js.map +1 -1
- package/dist/__tests__/react/useComponentState.test.d.ts +2 -0
- package/dist/__tests__/react/useComponentState.test.d.ts.map +1 -0
- package/dist/__tests__/react/useComponentState.test.js +68 -0
- package/dist/__tests__/react/useComponentState.test.js.map +1 -0
- package/dist/__tests__/react/useLQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useLQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useLQuery.test.js +38 -0
- package/dist/__tests__/react/useLQuery.test.js.map +1 -0
- package/dist/__tests__/react/useLiveStoreComponent.test.js +4 -9
- package/dist/__tests__/react/useLiveStoreComponent.test.js.map +1 -1
- package/dist/__tests__/react/useQuery.test.d.ts +2 -0
- package/dist/__tests__/react/useQuery.test.d.ts.map +1 -0
- package/dist/__tests__/react/useQuery.test.js +33 -0
- package/dist/__tests__/react/useQuery.test.js.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts +2 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.d.ts.map +1 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js +38 -0
- package/dist/__tests__/react/utils/extractStackInfoFromStackTrace.test.js.map +1 -0
- package/dist/__tests__/reactive.test.js +168 -95
- package/dist/__tests__/reactive.test.js.map +1 -1
- package/dist/__tests__/reactiveQueries/sql.test.d.ts +2 -0
- package/dist/__tests__/reactiveQueries/sql.test.d.ts.map +1 -0
- package/dist/__tests__/reactiveQueries/sql.test.js +337 -0
- package/dist/__tests__/reactiveQueries/sql.test.js.map +1 -0
- package/dist/effect/LiveStore.d.ts +3 -9
- package/dist/effect/LiveStore.d.ts.map +1 -1
- package/dist/effect/LiveStore.js +11 -7
- package/dist/effect/LiveStore.js.map +1 -1
- package/dist/inMemoryDatabase.d.ts +17 -21
- package/dist/inMemoryDatabase.d.ts.map +1 -1
- package/dist/inMemoryDatabase.js +2 -9
- package/dist/inMemoryDatabase.js.map +1 -1
- package/dist/index.d.ts +9 -7
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/migrations.d.ts +7 -0
- package/dist/migrations.d.ts.map +1 -1
- package/dist/migrations.js +18 -13
- package/dist/migrations.js.map +1 -1
- package/dist/react/LiveStoreProvider.d.ts +1 -3
- package/dist/react/LiveStoreProvider.d.ts.map +1 -1
- package/dist/react/LiveStoreProvider.js +13 -10
- package/dist/react/LiveStoreProvider.js.map +1 -1
- package/dist/react/index.d.ts +4 -4
- package/dist/react/index.d.ts.map +1 -1
- package/dist/react/index.js +3 -3
- package/dist/react/index.js.map +1 -1
- package/dist/react/useComponentState.d.ts +50 -0
- package/dist/react/useComponentState.d.ts.map +1 -0
- package/dist/react/useComponentState.js +248 -0
- package/dist/react/useComponentState.js.map +1 -0
- package/dist/react/useGlobalQuery.d.ts +2 -2
- package/dist/react/useGlobalQuery.d.ts.map +1 -1
- package/dist/react/useGlobalQuery.js +5 -2
- package/dist/react/useGlobalQuery.js.map +1 -1
- package/dist/react/useGraphQL.d.ts +5 -3
- package/dist/react/useGraphQL.d.ts.map +1 -1
- package/dist/react/useGraphQL.js +27 -7
- package/dist/react/useGraphQL.js.map +1 -1
- package/dist/react/useLiveStoreComponent.d.ts +14 -14
- package/dist/react/useLiveStoreComponent.d.ts.map +1 -1
- package/dist/react/useLiveStoreComponent.js +151 -91
- package/dist/react/useLiveStoreComponent.js.map +1 -1
- package/dist/react/useQuery.d.ts +3 -0
- package/dist/react/useQuery.d.ts.map +1 -0
- package/dist/react/useQuery.js +42 -0
- package/dist/react/useQuery.js.map +1 -0
- package/dist/react/useTemporaryQuery.d.ts +8 -0
- package/dist/react/useTemporaryQuery.d.ts.map +1 -0
- package/dist/react/useTemporaryQuery.js +17 -0
- package/dist/react/useTemporaryQuery.js.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts +3 -0
- package/dist/react/utils/extractNamesFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractNamesFromStackTrace.js +40 -0
- package/dist/react/utils/extractNamesFromStackTrace.js.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts +7 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.d.ts.map +1 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js +40 -0
- package/dist/react/utils/extractStackInfoFromStackTrace.js.map +1 -0
- package/dist/reactive.d.ts +42 -48
- package/dist/reactive.d.ts.map +1 -1
- package/dist/reactive.js +293 -186
- package/dist/reactive.js.map +1 -1
- package/dist/reactiveQueries/base-class.d.ts +28 -20
- package/dist/reactiveQueries/base-class.d.ts.map +1 -1
- package/dist/reactiveQueries/base-class.js +25 -17
- package/dist/reactiveQueries/base-class.js.map +1 -1
- package/dist/reactiveQueries/graph.d.ts +10 -0
- package/dist/reactiveQueries/graph.d.ts.map +1 -0
- package/dist/reactiveQueries/graph.js +6 -0
- package/dist/reactiveQueries/graph.js.map +1 -0
- package/dist/reactiveQueries/graphql.d.ts +35 -18
- package/dist/reactiveQueries/graphql.d.ts.map +1 -1
- package/dist/reactiveQueries/graphql.js +91 -10
- package/dist/reactiveQueries/graphql.js.map +1 -1
- package/dist/reactiveQueries/js.d.ts +17 -13
- package/dist/reactiveQueries/js.d.ts.map +1 -1
- package/dist/reactiveQueries/js.js +31 -8
- package/dist/reactiveQueries/js.js.map +1 -1
- package/dist/reactiveQueries/sql.d.ts +22 -18
- package/dist/reactiveQueries/sql.d.ts.map +1 -1
- package/dist/reactiveQueries/sql.js +81 -16
- package/dist/reactiveQueries/sql.js.map +1 -1
- package/dist/schema.d.ts +0 -2
- package/dist/schema.d.ts.map +1 -1
- package/dist/schema.js +3 -6
- package/dist/schema.js.map +1 -1
- package/dist/storage/in-memory/index.d.ts +2 -2
- package/dist/storage/in-memory/index.d.ts.map +1 -1
- package/dist/storage/in-memory/index.js.map +1 -1
- package/dist/storage/index.d.ts +2 -2
- package/dist/storage/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.d.ts +2 -2
- package/dist/storage/tauri/index.d.ts.map +1 -1
- package/dist/storage/tauri/index.js.map +1 -1
- package/dist/storage/web-worker/index.d.ts +4 -4
- package/dist/storage/web-worker/index.d.ts.map +1 -1
- package/dist/storage/web-worker/index.js +3 -5
- package/dist/storage/web-worker/index.js.map +1 -1
- package/dist/storage/web-worker/worker.js +2 -2
- package/dist/storage/web-worker/worker.js.map +1 -1
- package/dist/store.d.ts +19 -52
- package/dist/store.d.ts.map +1 -1
- package/dist/store.js +323 -266
- package/dist/store.js.map +1 -1
- package/dist/util.d.ts +3 -1
- package/dist/util.d.ts.map +1 -1
- package/dist/util.js +2 -0
- package/dist/util.js.map +1 -1
- package/package.json +2 -1
- package/src/QueryCache.ts +1 -1
- package/src/__tests__/react/fixture.tsx +21 -16
- package/src/__tests__/react/{useLiveStoreComponent.test.tsx → useComponentState.test.tsx} +9 -20
- package/src/__tests__/react/useQuery.test.tsx +48 -0
- package/src/__tests__/react/utils/extractStackInfoFromStackTrace.test.ts +40 -0
- package/src/__tests__/reactive.test.ts +194 -142
- package/src/__tests__/reactiveQueries/sql.test.ts +372 -0
- package/src/effect/LiveStore.ts +14 -18
- package/src/inMemoryDatabase.ts +22 -30
- package/src/index.ts +8 -6
- package/src/migrations.ts +39 -21
- package/src/react/LiveStoreProvider.tsx +13 -16
- package/src/react/index.ts +4 -8
- package/src/react/{useLiveStoreComponent.ts → useComponentState.ts} +98 -230
- package/src/react/useQuery.ts +58 -0
- package/src/react/useTemporaryQuery.ts +21 -0
- package/src/react/utils/extractStackInfoFromStackTrace.ts +47 -0
- package/src/reactive.ts +386 -267
- package/src/reactiveQueries/base-class.ts +61 -39
- package/src/reactiveQueries/graph.ts +15 -0
- package/src/reactiveQueries/graphql.ts +147 -31
- package/src/reactiveQueries/js.ts +54 -21
- package/src/reactiveQueries/sql.ts +128 -37
- package/src/schema.ts +2 -5
- package/src/storage/in-memory/index.ts +2 -2
- package/src/storage/index.ts +2 -2
- package/src/storage/tauri/index.ts +2 -2
- package/src/storage/web-worker/index.ts +6 -8
- package/src/storage/web-worker/worker.ts +2 -2
- package/src/store.ts +394 -418
- package/src/util.ts +8 -2
- package/dist/backends/base.d.ts +0 -13
- package/dist/backends/base.d.ts.map +0 -1
- package/dist/backends/base.js +0 -53
- package/dist/backends/base.js.map +0 -1
- package/dist/backends/in-memory/index.d.ts +0 -22
- package/dist/backends/in-memory/index.d.ts.map +0 -1
- package/dist/backends/in-memory/index.js +0 -45
- package/dist/backends/in-memory/index.js.map +0 -1
- package/dist/backends/index.d.ts +0 -41
- package/dist/backends/index.d.ts.map +0 -1
- package/dist/backends/index.js +0 -16
- package/dist/backends/index.js.map +0 -1
- package/dist/backends/tauri/index.d.ts +0 -21
- package/dist/backends/tauri/index.d.ts.map +0 -1
- package/dist/backends/tauri/index.js +0 -48
- package/dist/backends/tauri/index.js.map +0 -1
- package/dist/backends/utils/idb.d.ts +0 -10
- package/dist/backends/utils/idb.d.ts.map +0 -1
- package/dist/backends/utils/idb.js +0 -58
- package/dist/backends/utils/idb.js.map +0 -1
- package/dist/backends/web-worker/index.d.ts +0 -26
- package/dist/backends/web-worker/index.d.ts.map +0 -1
- package/dist/backends/web-worker/index.js +0 -63
- package/dist/backends/web-worker/index.js.map +0 -1
- package/dist/backends/web-worker/worker.d.ts +0 -17
- package/dist/backends/web-worker/worker.d.ts.map +0 -1
- package/dist/backends/web-worker/worker.js +0 -139
- package/dist/backends/web-worker/worker.js.map +0 -1
- package/dist/storage/base.d.ts +0 -10
- package/dist/storage/base.d.ts.map +0 -1
- package/dist/storage/base.js +0 -14
- package/dist/storage/base.js.map +0 -1
- package/src/react/useGlobalQuery.ts +0 -37
- package/src/react/useGraphQL.ts +0 -112
|
@@ -1,14 +1,10 @@
|
|
|
1
1
|
import type * as otel from '@opentelemetry/api'
|
|
2
|
-
import { mapValues } from 'lodash-es'
|
|
3
2
|
import type { ReactElement, ReactNode } from 'react'
|
|
4
3
|
import React from 'react'
|
|
4
|
+
import initSqlite3Wasm from 'sqlite-esm'
|
|
5
5
|
|
|
6
6
|
// TODO refactor so the `react` module doesn't depend on `effect` module
|
|
7
|
-
import type {
|
|
8
|
-
GlobalQueryDefs,
|
|
9
|
-
LiveStoreContext as StoreContext_,
|
|
10
|
-
LiveStoreCreateStoreOptions,
|
|
11
|
-
} from '../effect/LiveStore.js'
|
|
7
|
+
import type { LiveStoreContext as StoreContext_, LiveStoreCreateStoreOptions } from '../effect/LiveStore.js'
|
|
12
8
|
import type { InMemoryDatabase } from '../inMemoryDatabase.js'
|
|
13
9
|
import type { Schema } from '../schema.js'
|
|
14
10
|
import type { StorageInit } from '../storage/index.js'
|
|
@@ -16,11 +12,17 @@ import type { BaseGraphQLContext, GraphQLOptions } from '../store.js'
|
|
|
16
12
|
import { createStore } from '../store.js'
|
|
17
13
|
import { LiveStoreContext } from './LiveStoreContext.js'
|
|
18
14
|
|
|
15
|
+
// NOTE we're starting to initialize the sqlite wasm binary here (already before calling `createStore`),
|
|
16
|
+
// so that it's ready when we need it
|
|
17
|
+
const sqlite3Promise = initSqlite3Wasm({
|
|
18
|
+
print: (message) => console.log(`[livestore sqlite] ${message}`),
|
|
19
|
+
printErr: (message) => console.error(`[livestore sqlite] ${message}`),
|
|
20
|
+
})
|
|
21
|
+
|
|
19
22
|
interface LiveStoreProviderProps<GraphQLContext> {
|
|
20
23
|
schema: Schema
|
|
21
24
|
loadStorage: () => StorageInit | Promise<StorageInit>
|
|
22
25
|
boot?: (db: InMemoryDatabase, parentSpan: otel.Span) => unknown | Promise<unknown>
|
|
23
|
-
globalQueryDefs: GlobalQueryDefs
|
|
24
26
|
graphQLOptions?: GraphQLOptions<GraphQLContext>
|
|
25
27
|
otelTracer?: otel.Tracer
|
|
26
28
|
otelRootSpanContext?: otel.Context
|
|
@@ -29,7 +31,6 @@ interface LiveStoreProviderProps<GraphQLContext> {
|
|
|
29
31
|
|
|
30
32
|
export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
31
33
|
fallback,
|
|
32
|
-
globalQueryDefs,
|
|
33
34
|
loadStorage,
|
|
34
35
|
graphQLOptions,
|
|
35
36
|
otelTracer,
|
|
@@ -40,7 +41,6 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
40
41
|
}: LiveStoreProviderProps<GraphQLContext> & { children?: ReactNode }): JSX.Element => {
|
|
41
42
|
const store = useCreateStore({
|
|
42
43
|
schema,
|
|
43
|
-
globalQueryDefs,
|
|
44
44
|
loadStorage,
|
|
45
45
|
graphQLOptions,
|
|
46
46
|
otelTracer,
|
|
@@ -59,7 +59,6 @@ export const LiveStoreProvider = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
59
59
|
|
|
60
60
|
const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
61
61
|
schema,
|
|
62
|
-
globalQueryDefs,
|
|
63
62
|
loadStorage,
|
|
64
63
|
graphQLOptions,
|
|
65
64
|
otelTracer,
|
|
@@ -71,6 +70,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
71
70
|
React.useEffect(() => {
|
|
72
71
|
void (async () => {
|
|
73
72
|
try {
|
|
73
|
+
const sqlite3 = await sqlite3Promise
|
|
74
74
|
const store = await createStore({
|
|
75
75
|
schema,
|
|
76
76
|
loadStorage,
|
|
@@ -78,12 +78,9 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
78
78
|
otelTracer,
|
|
79
79
|
otelRootSpanContext,
|
|
80
80
|
boot,
|
|
81
|
+
sqlite3,
|
|
81
82
|
})
|
|
82
|
-
|
|
83
|
-
const globalQueries = mapValues(globalQueryDefs, (queryDef) => queryDef(store))
|
|
84
|
-
setCtxValue({ store, globalQueries })
|
|
85
|
-
span.end()
|
|
86
|
-
})
|
|
83
|
+
setCtxValue({ store })
|
|
87
84
|
} catch (e) {
|
|
88
85
|
console.error(`Error creating LiveStore store:`, e)
|
|
89
86
|
throw e
|
|
@@ -91,7 +88,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
91
88
|
})()
|
|
92
89
|
|
|
93
90
|
// TODO: do we need to return any cleanup function here?
|
|
94
|
-
}, [schema, loadStorage,
|
|
91
|
+
}, [schema, loadStorage, graphQLOptions, otelTracer, otelRootSpanContext, boot])
|
|
95
92
|
|
|
96
93
|
return ctxValue
|
|
97
94
|
}
|
package/src/react/index.ts
CHANGED
|
@@ -1,20 +1,16 @@
|
|
|
1
1
|
export type {
|
|
2
|
-
UseLiveStoreComponentProps as LiveStoreComponentConfig,
|
|
3
|
-
ReactiveGraphQL,
|
|
4
|
-
ReactiveSQL,
|
|
5
2
|
Setters,
|
|
6
3
|
ComponentKeyConfig,
|
|
7
|
-
QueryResults,
|
|
8
4
|
QueryDefinitions,
|
|
9
5
|
ComponentColumns,
|
|
10
6
|
GetStateType,
|
|
11
7
|
GetStateTypeEncoded,
|
|
12
|
-
} from './
|
|
8
|
+
} from './useComponentState.js'
|
|
13
9
|
export { LiveStoreContext, useStore } from './LiveStoreContext.js'
|
|
14
10
|
export { LiveStoreProvider } from './LiveStoreProvider.js'
|
|
15
|
-
export {
|
|
16
|
-
export {
|
|
17
|
-
export {
|
|
11
|
+
export { useComponentState } from './useComponentState.js'
|
|
12
|
+
export { useQuery } from './useQuery.js'
|
|
13
|
+
export { useTemporaryQuery } from './useTemporaryQuery.js'
|
|
18
14
|
|
|
19
15
|
// Needed to make TS happy
|
|
20
16
|
export type { TypedDocumentNode } from '@graphql-typed-document-node/core'
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
import type { LiteralUnion, PrettifyFlat } from '@livestore/utils'
|
|
1
|
+
import type { LiteralUnion } from '@livestore/utils'
|
|
3
2
|
import { omit, shouldNeverHappen } from '@livestore/utils'
|
|
4
3
|
import { Schema } from '@livestore/utils/effect'
|
|
5
4
|
import * as otel from '@opentelemetry/api'
|
|
6
|
-
import { SqliteDsl } from 'effect-db-schema'
|
|
5
|
+
import { SqliteAst, SqliteDsl } from 'effect-db-schema'
|
|
7
6
|
import { isEqual, mapValues } from 'lodash-es'
|
|
8
7
|
import type { DependencyList } from 'react'
|
|
9
8
|
import React from 'react'
|
|
@@ -11,60 +10,22 @@ import { v4 as uuid } from 'uuid'
|
|
|
11
10
|
|
|
12
11
|
import type { ComponentKey } from '../componentKey.js'
|
|
13
12
|
import { labelForKey, tableNameForComponentKey } from '../componentKey.js'
|
|
14
|
-
import
|
|
15
|
-
import
|
|
16
|
-
import
|
|
17
|
-
import
|
|
18
|
-
import type { BaseGraphQLContext, LiveStoreQuery,
|
|
19
|
-
import type { Bindable } from '../util.js'
|
|
13
|
+
import { migrateTable } from '../migrations.js'
|
|
14
|
+
import { LiveStoreJSQuery } from '../reactiveQueries/js.js'
|
|
15
|
+
import { LiveStoreSQLQuery } from '../reactiveQueries/sql.js'
|
|
16
|
+
import { SCHEMA_META_TABLE } from '../schema.js'
|
|
17
|
+
import type { BaseGraphQLContext, LiveStoreQuery, Store } from '../store.js'
|
|
20
18
|
import { sql } from '../util.js'
|
|
21
19
|
import { useStore } from './LiveStoreContext.js'
|
|
20
|
+
import { extractStackInfoFromStackTrace, originalStackLimit } from './utils/extractStackInfoFromStackTrace.js'
|
|
22
21
|
import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.js'
|
|
23
22
|
|
|
24
23
|
export interface QueryDefinitions {
|
|
25
24
|
[queryName: string]: LiveStoreQuery
|
|
26
25
|
}
|
|
27
26
|
|
|
28
|
-
export type
|
|
29
|
-
|
|
30
|
-
export type ReactiveSQL = <TResult>(
|
|
31
|
-
genQuery: (get: GetAtom) => string,
|
|
32
|
-
queriedTables: string[],
|
|
33
|
-
bindValues?: Bindable | undefined,
|
|
34
|
-
) => LiveStoreSQLQuery<TResult>
|
|
35
|
-
export type ReactiveGraphQL = <
|
|
36
|
-
TResult extends Record<string, any>,
|
|
37
|
-
TVariables extends Record<string, any>,
|
|
38
|
-
TContext extends BaseGraphQLContext,
|
|
39
|
-
>(
|
|
40
|
-
query: DocumentNode<TResult, TVariables>,
|
|
41
|
-
genVariableValues: (get: GetAtom) => TVariables,
|
|
42
|
-
label?: string,
|
|
43
|
-
) => LiveStoreGraphQLQuery<TResult, TVariables, TContext>
|
|
44
|
-
|
|
45
|
-
type RegisterSubscription = <TQuery extends LiveStoreQuery>(
|
|
46
|
-
query: TQuery,
|
|
47
|
-
onNewValue: (value: QueryResult<TQuery>) => void,
|
|
48
|
-
onUnsubscribe?: () => void,
|
|
49
|
-
) => void
|
|
50
|
-
|
|
51
|
-
type GenQueries<TQueries, TStateResult> = (args: {
|
|
52
|
-
rxSQL: ReactiveSQL
|
|
53
|
-
rxGraphQL: ReactiveGraphQL
|
|
54
|
-
globalQueries: QueryDefinitions
|
|
55
|
-
state$: LiveStoreJSQuery<TStateResult>
|
|
56
|
-
/**
|
|
57
|
-
* Registers a subscription.
|
|
58
|
-
*
|
|
59
|
-
* Passed down for some manual subscribing. Use carefully.
|
|
60
|
-
*/
|
|
61
|
-
subscribe: RegisterSubscription
|
|
62
|
-
isTemporaryQuery: boolean
|
|
63
|
-
}) => TQueries
|
|
64
|
-
|
|
65
|
-
export type UseLiveStoreComponentProps<TQueries, TColumns extends ComponentColumns> = {
|
|
66
|
-
stateSchema?: SqliteDsl.TableDefinition<string, TColumns>
|
|
67
|
-
queries?: GenQueries<TQueries, SqliteDsl.FromColumns.RowDecoded<TColumns>>
|
|
27
|
+
export type UseComponentStateProps<TStateColumns extends ComponentColumns> = {
|
|
28
|
+
schema?: SqliteDsl.TableDefinition<string, TStateColumns>
|
|
68
29
|
reactDeps?: React.DependencyList
|
|
69
30
|
componentKey: ComponentKeyConfig
|
|
70
31
|
}
|
|
@@ -115,18 +76,17 @@ export type GetStateTypeEncoded<TTableDef extends SqliteDsl.TableDefinition<any,
|
|
|
115
76
|
* @param config.componentKey A function that returns a unique key for this component.
|
|
116
77
|
* @param config.reactDeps A list of React-level dependencies that will refresh the queries.
|
|
117
78
|
*/
|
|
118
|
-
export const
|
|
119
|
-
|
|
120
|
-
queries = () => ({}) as TQueries,
|
|
79
|
+
export const useComponentState = <TStateColumns extends ComponentColumns>({
|
|
80
|
+
schema: stateSchema_,
|
|
121
81
|
componentKey: componentKeyConfig,
|
|
122
82
|
reactDeps = [],
|
|
123
|
-
}:
|
|
124
|
-
|
|
125
|
-
state: SqliteDsl.FromColumns.RowDecoded<
|
|
126
|
-
setState: Setters<SqliteDsl.FromColumns.RowDecoded<
|
|
127
|
-
useLiveStoreJsonState: UseLiveStoreJsonState<SqliteDsl.FromColumns.RowDecoded<
|
|
83
|
+
}: UseComponentStateProps<TStateColumns>): {
|
|
84
|
+
state$: LiveStoreJSQuery<SqliteDsl.FromColumns.RowDecoded<TStateColumns>>
|
|
85
|
+
state: SqliteDsl.FromColumns.RowDecoded<TStateColumns>
|
|
86
|
+
setState: Setters<SqliteDsl.FromColumns.RowDecoded<TStateColumns>>
|
|
87
|
+
useLiveStoreJsonState: UseLiveStoreJsonState<SqliteDsl.FromColumns.RowDecoded<TStateColumns>>
|
|
128
88
|
} => {
|
|
129
|
-
type TComponentState = SqliteDsl.FromColumns.RowDecoded<
|
|
89
|
+
type TComponentState = SqliteDsl.FromColumns.RowDecoded<TStateColumns>
|
|
130
90
|
|
|
131
91
|
// TODO validate schema to make sure each column has a default value
|
|
132
92
|
// TODO we should clean up the state schema handling to remove this special handling for the `id` column
|
|
@@ -135,9 +95,8 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
135
95
|
[stateSchema_],
|
|
136
96
|
)
|
|
137
97
|
|
|
138
|
-
// performance.mark('useLiveStoreComponent:start')
|
|
139
98
|
const componentKey = useComponentKey(componentKeyConfig, reactDeps)
|
|
140
|
-
const { store
|
|
99
|
+
const { store } = useStore()
|
|
141
100
|
|
|
142
101
|
const componentKeyLabel = React.useMemo(() => labelForKey(componentKey), [componentKey])
|
|
143
102
|
|
|
@@ -147,7 +106,7 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
147
106
|
if (existingSpan !== undefined) return existingSpan
|
|
148
107
|
|
|
149
108
|
const span = store.otel.tracer.startSpan(
|
|
150
|
-
`LiveStore:
|
|
109
|
+
`LiveStore:useComponentState:${componentKeyLabel}`,
|
|
151
110
|
{},
|
|
152
111
|
store.otel.queriesSpanContext,
|
|
153
112
|
)
|
|
@@ -167,41 +126,6 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
167
126
|
[componentKeyLabel, span],
|
|
168
127
|
)
|
|
169
128
|
|
|
170
|
-
const generateQueries = React.useCallback(
|
|
171
|
-
({
|
|
172
|
-
state$,
|
|
173
|
-
otelContext,
|
|
174
|
-
registerSubscription,
|
|
175
|
-
isTemporaryQuery,
|
|
176
|
-
}: {
|
|
177
|
-
state$: LiveStoreJSQuery<TComponentState>
|
|
178
|
-
otelContext: otel.Context
|
|
179
|
-
registerSubscription: RegisterSubscription
|
|
180
|
-
isTemporaryQuery: boolean
|
|
181
|
-
}) =>
|
|
182
|
-
queries({
|
|
183
|
-
rxSQL: <T>(genQuery: (get: GetAtom) => string, queriedTables: string[], bindValues?: Bindable) =>
|
|
184
|
-
store.querySQL<T>(genQuery, { queriedTables, bindValues, otelContext, componentKey }),
|
|
185
|
-
rxGraphQL: <Result extends Record<string, any>, Variables extends Record<string, any>>(
|
|
186
|
-
query: DocumentNode<Result, Variables>,
|
|
187
|
-
genVariableValues: (get: GetAtom) => Variables,
|
|
188
|
-
label?: string,
|
|
189
|
-
) => store.queryGraphQL(query, genVariableValues, { componentKey, label, otelContext }),
|
|
190
|
-
globalQueries,
|
|
191
|
-
state$,
|
|
192
|
-
subscribe: registerSubscription,
|
|
193
|
-
isTemporaryQuery,
|
|
194
|
-
}),
|
|
195
|
-
|
|
196
|
-
// NOTE: we don't include the queries function passed in by the user here;
|
|
197
|
-
// the reason is that we don't want to force them to memoize that function.
|
|
198
|
-
// Instead, we just assume that the function always has the same contents.
|
|
199
|
-
// This makes sense for LiveStore because the component config should be static.
|
|
200
|
-
// TODO: document this and consider whether it's the right API surface.
|
|
201
|
-
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
202
|
-
[store, componentKey, globalQueries],
|
|
203
|
-
)
|
|
204
|
-
|
|
205
129
|
const defaultComponentState = React.useMemo(() => {
|
|
206
130
|
const defaultState = (
|
|
207
131
|
stateSchema === undefined ? {} : mapValues(stateSchema.columns, (c) => c.default)
|
|
@@ -218,81 +142,68 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
218
142
|
[stateSchema],
|
|
219
143
|
)
|
|
220
144
|
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
// create state query
|
|
232
|
-
let state$: LiveStoreJSQuery<TComponentState>
|
|
233
|
-
if (stateSchema === undefined) {
|
|
234
|
-
// TODO don't set up a query if there's no state schema (keeps the graph more clean)
|
|
235
|
-
state$ = store.queryJS(() => ({}), {
|
|
236
|
-
componentKey,
|
|
237
|
-
otelContext,
|
|
238
|
-
}) as unknown as LiveStoreJSQuery<TComponentState>
|
|
239
|
-
} else {
|
|
240
|
-
const componentTableName = tableNameForComponentKey(componentKey)
|
|
241
|
-
const whereClause = componentKey._tag === 'singleton' ? '' : `where id = '${componentKey.id}'`
|
|
242
|
-
state$ = store
|
|
243
|
-
.querySQL(() => sql`select * from ${componentTableName} ${whereClause} limit 1`, {
|
|
244
|
-
queriedTables: [componentTableName],
|
|
245
|
-
componentKey,
|
|
246
|
-
label: `localState:query:${componentKeyLabel}`,
|
|
247
|
-
otelContext,
|
|
248
|
-
})
|
|
249
|
-
// TODO consider to instead of just returning the default value, to write the default component state to the DB
|
|
250
|
-
.pipe<TComponentState>((results) =>
|
|
251
|
-
results.length === 1
|
|
252
|
-
? Schema.parseSync(componentStateEffectSchema)(results[0]!)
|
|
253
|
-
: defaultComponentState,
|
|
254
|
-
)
|
|
255
|
-
}
|
|
256
|
-
const initialComponentState = state$.results$.result
|
|
257
|
-
|
|
258
|
-
const queries = generateQueries({
|
|
259
|
-
state$: state$,
|
|
260
|
-
otelContext,
|
|
261
|
-
registerSubscription: () => {},
|
|
262
|
-
isTemporaryQuery: true,
|
|
263
|
-
})
|
|
264
|
-
for (const [name, query] of Object.entries(queries)) {
|
|
265
|
-
query.label = name
|
|
266
|
-
}
|
|
267
|
-
const initialQueryResults = mapValues(
|
|
268
|
-
queries,
|
|
269
|
-
(query) => query.results$.result,
|
|
270
|
-
// TODO improve typing
|
|
271
|
-
) as unknown as QueryResults<TQueries>
|
|
272
|
-
|
|
273
|
-
return { initialComponentState, initialQueryResults }
|
|
274
|
-
} finally {
|
|
275
|
-
span.end()
|
|
276
|
-
}
|
|
145
|
+
const state$ = React.useMemo(() => {
|
|
146
|
+
console.log('useComponentState make state$', labelForKey(componentKey))
|
|
147
|
+
// create state query
|
|
148
|
+
if (stateSchema === undefined) {
|
|
149
|
+
// TODO don't set up a query if there's no state schema (keeps the graph more clean)
|
|
150
|
+
return new LiveStoreJSQuery({
|
|
151
|
+
fn: () => ({}) as TComponentState,
|
|
152
|
+
label: 'empty-component-state',
|
|
153
|
+
// otelContext,
|
|
154
|
+
// otelTracer: store.otel.tracer,
|
|
277
155
|
})
|
|
278
|
-
}
|
|
156
|
+
} else {
|
|
157
|
+
const componentTableName = tableNameForComponentKey(componentKey)
|
|
158
|
+
const whereClause = componentKey._tag === 'singleton' ? '' : `where id = '${componentKey.id}'`
|
|
159
|
+
|
|
160
|
+
// TODO find a better solution for this
|
|
161
|
+
if (store.tableRefs[componentTableName] === undefined) {
|
|
162
|
+
const schemaHash = SqliteAst.hash(stateSchema.ast)
|
|
163
|
+
const res = store.inMemoryDB.select<{ schemaHash: number }>(
|
|
164
|
+
sql`SELECT schemaHash FROM ${SCHEMA_META_TABLE} WHERE tableName = '${componentTableName}'`,
|
|
165
|
+
)
|
|
166
|
+
if (res.length === 0 || res[0]!.schemaHash !== schemaHash) {
|
|
167
|
+
migrateTable({ db: store._proxyDb, tableDef: stateSchema.ast, otelContext, schemaHash })
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
store.tableRefs[componentTableName] = store.graph.makeRef(null, {
|
|
171
|
+
equal: () => false,
|
|
172
|
+
label: componentTableName,
|
|
173
|
+
meta: { liveStoreRefType: 'table' },
|
|
174
|
+
})
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
return (
|
|
178
|
+
new LiveStoreSQLQuery({
|
|
179
|
+
label: `localState:query:${componentKeyLabel}`,
|
|
180
|
+
genQueryString: () => sql`select * from ${componentTableName} ${whereClause} limit 1`,
|
|
181
|
+
queriedTables: [componentTableName],
|
|
182
|
+
})
|
|
183
|
+
// TODO consider to instead of just returning the default value, to write the default component state to the DB
|
|
184
|
+
.pipe<TComponentState>((results) =>
|
|
185
|
+
results.length === 1 ? Schema.parseSync(componentStateEffectSchema)(results[0]!) : defaultComponentState,
|
|
186
|
+
)
|
|
187
|
+
)
|
|
188
|
+
}
|
|
279
189
|
}, [
|
|
280
|
-
store,
|
|
281
|
-
otelContext,
|
|
282
|
-
stateSchema,
|
|
283
|
-
generateQueries,
|
|
284
190
|
componentKey,
|
|
285
191
|
componentKeyLabel,
|
|
286
192
|
componentStateEffectSchema,
|
|
287
193
|
defaultComponentState,
|
|
194
|
+
otelContext,
|
|
195
|
+
stateSchema,
|
|
196
|
+
store,
|
|
288
197
|
])
|
|
289
198
|
|
|
199
|
+
// Step 1:
|
|
200
|
+
// Synchronously create state and queries for initial render pass.
|
|
201
|
+
const initialComponentState = React.useMemo(() => state$.run(otelContext), [otelContext, state$])
|
|
202
|
+
|
|
290
203
|
// Now that we've computed the initial state synchronously,
|
|
291
204
|
// we can set up our useState calls w/ a default value populated...
|
|
292
205
|
const [componentStateRef, setComponentState_] = useStateRefWithReactiveInput<TComponentState>(initialComponentState)
|
|
293
206
|
|
|
294
|
-
const [queryResultsRef, setQueryResults_] = useStateRefWithReactiveInput<QueryResults<TQueries>>(initialQueryResults)
|
|
295
|
-
|
|
296
207
|
const setState = (
|
|
297
208
|
stateSchema === undefined
|
|
298
209
|
? {}
|
|
@@ -329,44 +240,30 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
329
240
|
return store.applyEvent('updateComponentState', { componentKey, columnNames, ...columnValues })
|
|
330
241
|
}
|
|
331
242
|
|
|
243
|
+
const subscriptionInfo = React.useMemo(() => {
|
|
244
|
+
Error.stackTraceLimit = 10
|
|
245
|
+
// eslint-disable-next-line unicorn/error-message
|
|
246
|
+
const stack = new Error().stack!
|
|
247
|
+
Error.stackTraceLimit = originalStackLimit
|
|
248
|
+
return { stack: extractStackInfoFromStackTrace(stack) }
|
|
249
|
+
}, [])
|
|
250
|
+
|
|
332
251
|
// OK, now all the synchronous work is done;
|
|
333
252
|
// time to set up our long-running queries in an effect
|
|
334
253
|
React.useEffect(() => {
|
|
335
254
|
return store.otel.tracer.startActiveSpan(
|
|
336
|
-
'LiveStore:
|
|
255
|
+
'LiveStore:useComponentState:long-running',
|
|
337
256
|
{ attributes: {} },
|
|
338
257
|
otelContext,
|
|
339
258
|
(span) => {
|
|
340
|
-
const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
341
259
|
const unsubs: (() => void)[] = []
|
|
342
260
|
|
|
343
|
-
|
|
344
|
-
let state$: LiveStoreJSQuery<TComponentState>
|
|
345
|
-
if (stateSchema === undefined) {
|
|
346
|
-
// TODO remove this query
|
|
347
|
-
state$ = store.queryJS(() => ({}) as TComponentState, {
|
|
348
|
-
componentKey,
|
|
349
|
-
otelContext,
|
|
350
|
-
label: 'empty-component-state',
|
|
351
|
-
})
|
|
352
|
-
} else {
|
|
353
|
-
const componentTableName = tableNameForComponentKey(componentKey)
|
|
261
|
+
if (stateSchema !== undefined) {
|
|
354
262
|
insertRowForComponentInstance({ store, componentKey, stateSchema })
|
|
355
|
-
|
|
356
|
-
const whereClause = componentKey._tag === 'singleton' ? '' : `where id = '${componentKey.id}'`
|
|
357
|
-
state$ = store
|
|
358
|
-
.querySQL<TComponentState>(() => sql`select * from ${componentTableName} ${whereClause} limit 1`, {
|
|
359
|
-
queriedTables: [componentTableName],
|
|
360
|
-
componentKey,
|
|
361
|
-
label: `localState:query:${componentKeyLabel}`,
|
|
362
|
-
otelContext,
|
|
363
|
-
})
|
|
364
|
-
// TODO consider to instead of just returning the default value, to write the default component state to the DB
|
|
365
|
-
.pipe<TComponentState>((results) =>
|
|
366
|
-
results.length === 1 ? Schema.parseSync(componentStateEffectSchema)(results[0]!) : defaultComponentState,
|
|
367
|
-
)
|
|
368
263
|
}
|
|
369
264
|
|
|
265
|
+
state$.activeSubscriptions.add(subscriptionInfo)
|
|
266
|
+
|
|
370
267
|
unsubs.push(
|
|
371
268
|
store.subscribe(
|
|
372
269
|
state$,
|
|
@@ -376,44 +273,11 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
376
273
|
}
|
|
377
274
|
},
|
|
378
275
|
undefined,
|
|
379
|
-
{ label: `
|
|
276
|
+
{ label: `useComponentState:localState:subscribe:${state$.label}` },
|
|
380
277
|
),
|
|
278
|
+
() => state$.activeSubscriptions.delete(subscriptionInfo),
|
|
381
279
|
)
|
|
382
280
|
|
|
383
|
-
const registerSubscription: RegisterSubscription = (query, callback, onUnsubscribe) => {
|
|
384
|
-
unsubs.push(
|
|
385
|
-
store.subscribe(
|
|
386
|
-
query,
|
|
387
|
-
(results) => {
|
|
388
|
-
callback(results)
|
|
389
|
-
},
|
|
390
|
-
onUnsubscribe,
|
|
391
|
-
{ label: `useLiveStoreComponent:query:manual-subscribe:${query.label}` },
|
|
392
|
-
),
|
|
393
|
-
)
|
|
394
|
-
}
|
|
395
|
-
|
|
396
|
-
const queries = generateQueries({ state$, otelContext, registerSubscription, isTemporaryQuery: false })
|
|
397
|
-
|
|
398
|
-
for (const [key, query] of Object.entries(queries)) {
|
|
399
|
-
// Use the field name given to this query in the useQueries hook as its label
|
|
400
|
-
query.label = key
|
|
401
|
-
|
|
402
|
-
unsubs.push(
|
|
403
|
-
store.subscribe(
|
|
404
|
-
query,
|
|
405
|
-
(results) => {
|
|
406
|
-
const newQueryResults = { ...queryResultsRef.current, [key]: results }
|
|
407
|
-
if (isEqual(newQueryResults, queryResultsRef.current) === false) {
|
|
408
|
-
setQueryResults_(newQueryResults)
|
|
409
|
-
}
|
|
410
|
-
},
|
|
411
|
-
undefined,
|
|
412
|
-
{ label: `useLiveStoreComponent:query:subscribe:${query.label}` },
|
|
413
|
-
),
|
|
414
|
-
)
|
|
415
|
-
}
|
|
416
|
-
|
|
417
281
|
return () => {
|
|
418
282
|
for (const unsub of unsubs) {
|
|
419
283
|
unsub()
|
|
@@ -425,24 +289,28 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
425
289
|
)
|
|
426
290
|
// NOTE excluding `setComponentState_` and `setQueryResults_` from the deps array as it seems to cause an infinite loop
|
|
427
291
|
// This should probably be improved
|
|
428
|
-
//
|
|
292
|
+
// TODO is this still true?
|
|
293
|
+
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
429
294
|
}, [
|
|
430
295
|
store,
|
|
431
|
-
|
|
296
|
+
subscriptionInfo,
|
|
432
297
|
stateSchema,
|
|
433
298
|
defaultComponentState,
|
|
434
|
-
generateQueries,
|
|
435
299
|
otelContext,
|
|
436
300
|
componentStateRef,
|
|
437
|
-
|
|
438
|
-
|
|
301
|
+
state$,
|
|
302
|
+
setComponentState_,
|
|
303
|
+
componentKey,
|
|
439
304
|
])
|
|
440
305
|
|
|
441
306
|
// Very important: remove any queries / other resources associated w/ this component
|
|
442
|
-
React.useEffect(
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
307
|
+
React.useEffect(
|
|
308
|
+
() => () => {
|
|
309
|
+
console.log('useComponentState destroy', labelForKey(componentKey))
|
|
310
|
+
return state$.destroy()
|
|
311
|
+
},
|
|
312
|
+
[state$],
|
|
313
|
+
)
|
|
446
314
|
|
|
447
315
|
const state = componentStateRef.current
|
|
448
316
|
|
|
@@ -472,7 +340,7 @@ export const useLiveStoreComponent = <TColumns extends ComponentColumns, TQuerie
|
|
|
472
340
|
}
|
|
473
341
|
|
|
474
342
|
return {
|
|
475
|
-
|
|
343
|
+
state$,
|
|
476
344
|
state,
|
|
477
345
|
setState,
|
|
478
346
|
useLiveStoreJsonState,
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { isEqual } from 'lodash-es'
|
|
2
|
+
import React from 'react'
|
|
3
|
+
|
|
4
|
+
import type { ILiveStoreQuery } from '../reactiveQueries/base-class.js'
|
|
5
|
+
import { useStore } from './LiveStoreContext.js'
|
|
6
|
+
import { extractStackInfoFromStackTrace, originalStackLimit } from './utils/extractStackInfoFromStackTrace.js'
|
|
7
|
+
import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.js'
|
|
8
|
+
|
|
9
|
+
export const useQuery = <TResult>(query: ILiveStoreQuery<TResult>): TResult => {
|
|
10
|
+
const { store } = useStore()
|
|
11
|
+
|
|
12
|
+
// TODO proper otel context
|
|
13
|
+
const initialResult = React.useMemo(() => query.run(), [query])
|
|
14
|
+
|
|
15
|
+
// We know the query has a result by the time we use it; so we can synchronously populate a default state
|
|
16
|
+
const [valueRef, setValue] = useStateRefWithReactiveInput<TResult>(initialResult)
|
|
17
|
+
|
|
18
|
+
const subscriptionInfo = React.useMemo(() => {
|
|
19
|
+
Error.stackTraceLimit = 10
|
|
20
|
+
// eslint-disable-next-line unicorn/error-message
|
|
21
|
+
const stack = new Error().stack!
|
|
22
|
+
Error.stackTraceLimit = originalStackLimit
|
|
23
|
+
return { stack: extractStackInfoFromStackTrace(stack) }
|
|
24
|
+
}, [])
|
|
25
|
+
|
|
26
|
+
// Subscribe to future updates for this query
|
|
27
|
+
React.useEffect(() => {
|
|
28
|
+
return store.otel.tracer.startActiveSpan(
|
|
29
|
+
`LiveStore:useQuery:${query.label}`,
|
|
30
|
+
// `LiveStore:useQuery:${labelForKey(query.componentKey)}:${query.label}`,
|
|
31
|
+
{ attributes: { label: query.label } },
|
|
32
|
+
store.otel.queriesSpanContext,
|
|
33
|
+
(span) => {
|
|
34
|
+
query.activeSubscriptions.add(subscriptionInfo)
|
|
35
|
+
const unsub = store.subscribe(
|
|
36
|
+
query,
|
|
37
|
+
(v) => {
|
|
38
|
+
// NOTE: we return a reference to the result object within LiveStore;
|
|
39
|
+
// this implies that app code must not mutate the results, or else
|
|
40
|
+
// there may be weird reactivity bugs.
|
|
41
|
+
if (isEqual(v, valueRef.current) === false) {
|
|
42
|
+
setValue(v)
|
|
43
|
+
}
|
|
44
|
+
},
|
|
45
|
+
undefined,
|
|
46
|
+
{ label: query.label },
|
|
47
|
+
)
|
|
48
|
+
return () => {
|
|
49
|
+
query.activeSubscriptions.delete(subscriptionInfo)
|
|
50
|
+
unsub()
|
|
51
|
+
span.end()
|
|
52
|
+
}
|
|
53
|
+
},
|
|
54
|
+
)
|
|
55
|
+
}, [subscriptionInfo, query, setValue, store, valueRef])
|
|
56
|
+
|
|
57
|
+
return valueRef.current
|
|
58
|
+
}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import React from 'react'
|
|
2
|
+
|
|
3
|
+
import type { ILiveStoreQuery } from '../reactiveQueries/base-class.js'
|
|
4
|
+
import { useQuery } from './useQuery.js'
|
|
5
|
+
|
|
6
|
+
/**
|
|
7
|
+
* Creates a query, subscribes and destroys it when the component unmounts.
|
|
8
|
+
*
|
|
9
|
+
* Make sure `makeQuery` is a memoized function.
|
|
10
|
+
*/
|
|
11
|
+
export const useTemporaryQuery = <TResult>(makeQuery: () => ILiveStoreQuery<TResult>): TResult => {
|
|
12
|
+
const query = React.useMemo(() => makeQuery(), [makeQuery])
|
|
13
|
+
|
|
14
|
+
React.useEffect(() => {
|
|
15
|
+
return () => {
|
|
16
|
+
query.destroy()
|
|
17
|
+
}
|
|
18
|
+
}, [query])
|
|
19
|
+
|
|
20
|
+
return useQuery(query)
|
|
21
|
+
}
|