@livestore/react 0.3.0-dev.10 → 0.3.0-dev.12
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 +5 -3
- package/dist/LiveStoreContext.d.ts.map +1 -1
- package/dist/LiveStoreContext.js +7 -3
- package/dist/LiveStoreContext.js.map +1 -1
- package/dist/LiveStoreProvider.d.ts +5 -2
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +2 -17
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +6 -8
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +6 -7
- 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 +5 -4
- package/dist/experimental/components/LiveList.js.map +1 -1
- package/dist/mod.d.ts +0 -1
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +0 -1
- package/dist/mod.js.map +1 -1
- package/dist/useAtom.d.ts +4 -2
- package/dist/useAtom.d.ts.map +1 -1
- package/dist/useAtom.js +32 -28
- package/dist/useAtom.js.map +1 -1
- package/dist/useQuery.d.ts +26 -3
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js +60 -45
- package/dist/useQuery.js.map +1 -1
- package/dist/useQuery.test.js +70 -16
- package/dist/useQuery.test.js.map +1 -1
- package/dist/useRcRef.d.ts +72 -0
- package/dist/useRcRef.d.ts.map +1 -0
- package/dist/useRcRef.js +146 -0
- package/dist/useRcRef.js.map +1 -0
- package/dist/useRcRef.test.d.ts +2 -0
- package/dist/useRcRef.test.d.ts.map +1 -0
- package/dist/useRcRef.test.js +128 -0
- package/dist/useRcRef.test.js.map +1 -0
- package/dist/useRcResource.d.ts +76 -0
- package/dist/useRcResource.d.ts.map +1 -0
- package/dist/useRcResource.js +150 -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/useRow.d.ts +10 -7
- package/dist/useRow.d.ts.map +1 -1
- package/dist/useRow.js +23 -22
- package/dist/useRow.js.map +1 -1
- package/dist/useRow.test.js +62 -80
- package/dist/useRow.test.js.map +1 -1
- package/dist/useScopedQuery.d.ts +10 -4
- package/dist/useScopedQuery.d.ts.map +1 -1
- package/dist/useScopedQuery.js +96 -52
- package/dist/useScopedQuery.js.map +1 -1
- package/dist/useScopedQuery.test.js +13 -12
- package/dist/useScopedQuery.test.js.map +1 -1
- package/package.json +6 -6
- package/src/LiveStoreContext.ts +10 -6
- package/src/LiveStoreProvider.tsx +3 -19
- package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
- package/src/__snapshots__/useRow.test.tsx.snap +335 -142
- package/src/__tests__/fixture.tsx +6 -9
- package/src/experimental/components/LiveList.tsx +8 -7
- package/src/mod.ts +0 -1
- package/src/useAtom.ts +22 -11
- package/src/useQuery.test.tsx +165 -67
- package/src/useQuery.ts +84 -54
- package/src/useRcResource.test.tsx +167 -0
- package/src/useRcResource.ts +180 -0
- package/src/useRow.test.tsx +73 -107
- package/src/useRow.ts +42 -40
- package/src/useScopedQuery.test.tsx +0 -96
- package/src/useScopedQuery.ts +0 -143
package/dist/useScopedQuery.js
CHANGED
|
@@ -2,6 +2,7 @@ import * as otel from '@opentelemetry/api';
|
|
|
2
2
|
import React from 'react';
|
|
3
3
|
import { useStore } from './LiveStoreContext.js';
|
|
4
4
|
import { useQueryRef } from './useQuery.js';
|
|
5
|
+
import { useRcRef } from './useRcResource.js';
|
|
5
6
|
// NOTE Given `useMemo` will be called multiple times (e.g. when using React Strict mode or Fast Refresh),
|
|
6
7
|
// we are using this cache to avoid starting multiple queries/spans for the same component.
|
|
7
8
|
// This is somewhat against some recommended React best practices, but it should be fine in our case below.
|
|
@@ -23,65 +24,108 @@ const cache = new Map();
|
|
|
23
24
|
* Example: `['issue-details', issueId]`
|
|
24
25
|
* See this issue to track progress: https://github.com/livestorejs/livestore/issues/231
|
|
25
26
|
*/
|
|
26
|
-
export const useScopedQuery = (makeQuery, key) => useScopedQueryRef(makeQuery, key).current;
|
|
27
|
-
export const useScopedQueryRef = (makeQuery, key) => {
|
|
28
|
-
const { query$ } = useMakeScopedQuery(makeQuery, key);
|
|
29
|
-
return useQueryRef(query$);
|
|
27
|
+
export const useScopedQuery = (makeQuery, key, options) => useScopedQueryRef(makeQuery, key, options).current;
|
|
28
|
+
export const useScopedQueryRef = (makeQuery, key, options) => {
|
|
29
|
+
const { query$ } = useMakeScopedQuery(makeQuery, key, options);
|
|
30
|
+
return useQueryRef(query$)[0];
|
|
30
31
|
};
|
|
31
|
-
export const useMakeScopedQuery = (
|
|
32
|
-
const { store } = useStore();
|
|
32
|
+
export const useMakeScopedQuery = (makeQueryDef, key, options) => {
|
|
33
|
+
const { store } = useStore({ store: options?.store });
|
|
33
34
|
const fullKey = React.useMemo(
|
|
34
35
|
// NOTE We're using the `makeQuery` function body string to make sure the key is unique across the app
|
|
35
36
|
// TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
const {
|
|
39
|
-
if (fullKeyRef.current !== undefined && fullKeyRef.current !== fullKey) {
|
|
40
|
-
// console.debug('fullKey changed', 'prev', fullKeyRef.current.split('-')[0]!, '-> new', fullKey.split('-')[0]!)
|
|
41
|
-
const cachedItem = cache.get(fullKeyRef.current);
|
|
42
|
-
if (cachedItem !== undefined && cachedItem._tag === 'active') {
|
|
43
|
-
cachedItem.rc--;
|
|
44
|
-
if (cachedItem.rc === 0) {
|
|
45
|
-
// console.debug('rc=0-changed', cachedItem.query$.id, cachedItem.query$.label)
|
|
46
|
-
cachedItem.query$.destroy();
|
|
47
|
-
cachedItem.span.end();
|
|
48
|
-
cache.set(fullKeyRef.current, { _tag: 'destroyed' });
|
|
49
|
-
}
|
|
50
|
-
}
|
|
51
|
-
}
|
|
52
|
-
const cachedItem = cache.get(fullKey);
|
|
53
|
-
if (cachedItem !== undefined && cachedItem._tag === 'active') {
|
|
54
|
-
// console.debug('rc++', cachedItem.query$.id, cachedItem.query$.label)
|
|
55
|
-
cachedItem.rc++;
|
|
56
|
-
return cachedItem;
|
|
57
|
-
}
|
|
37
|
+
// NOTE `makeQueryDef.toString()` doesn't work in Expo as it always produces `[native code]`
|
|
38
|
+
() => (Array.isArray(key) ? key.join('-') : key) + '-' + store.reactivityGraph.id + '-' + makeQueryDef.toString(), [key, makeQueryDef, store.reactivityGraph.id]);
|
|
39
|
+
const { queryRef, queryDef, otelContext } = useRcRef(fullKey, () => {
|
|
58
40
|
const spanName = options?.otel?.spanName ?? `LiveStore:useScopedQuery:${key}`;
|
|
59
41
|
const span = store.otel.tracer.startSpan(spanName, { attributes: options?.otel?.attributes }, store.otel.queriesSpanContext);
|
|
60
42
|
const otelContext = otel.trace.setSpan(otel.context.active(), span);
|
|
61
43
|
// console.debug('useScopedQuery:startSpan', fullKey, spanName)
|
|
62
|
-
const
|
|
63
|
-
|
|
64
|
-
return {
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
const fullKey = fullKeyRef.current;
|
|
71
|
-
const cachedItem = cache.get(fullKey);
|
|
72
|
-
// NOTE in case the fullKey changed then the query was already destroyed in the useMemo above
|
|
73
|
-
if (cachedItem === undefined || cachedItem._tag === 'destroyed')
|
|
74
|
-
return;
|
|
75
|
-
// console.debug('rc--', cachedItem.query$.id, cachedItem.query$.label)
|
|
76
|
-
cachedItem.rc--;
|
|
77
|
-
if (cachedItem.rc === 0) {
|
|
78
|
-
// console.debug('rc=0', cachedItem.query$.id, cachedItem.query$.label)
|
|
79
|
-
cachedItem.query$.destroy();
|
|
80
|
-
cachedItem.span.end();
|
|
81
|
-
cache.delete(fullKey);
|
|
82
|
-
}
|
|
83
|
-
};
|
|
84
|
-
}, []);
|
|
85
|
-
return { query$, otelContext };
|
|
44
|
+
const queryDef = makeQueryDef(otelContext);
|
|
45
|
+
const queryRef = queryDef.make(store.reactivityGraph.context);
|
|
46
|
+
return { queryRef, queryDef, otelContext, span };
|
|
47
|
+
}, ({ queryRef, span }) => {
|
|
48
|
+
queryRef.deref();
|
|
49
|
+
span.end();
|
|
50
|
+
});
|
|
51
|
+
return { query$: queryRef.value, queryDef, otelContext };
|
|
86
52
|
};
|
|
53
|
+
// export const useMakeScopedQuery = <TResult, TQueryInfo extends QueryInfo>(
|
|
54
|
+
// makeQueryDef: (otelContext: otel.Context) => LiveQueryDef<TResult, TQueryInfo>,
|
|
55
|
+
// key: DepKey,
|
|
56
|
+
// options?: {
|
|
57
|
+
// store?: Store
|
|
58
|
+
// otel?: {
|
|
59
|
+
// spanName?: string
|
|
60
|
+
// attributes?: otel.Attributes
|
|
61
|
+
// }
|
|
62
|
+
// },
|
|
63
|
+
// ): {
|
|
64
|
+
// query$: LiveQuery<TResult, TQueryInfo>
|
|
65
|
+
// queryDef: LiveQueryDef<TResult, TQueryInfo>
|
|
66
|
+
// otelContext: otel.Context
|
|
67
|
+
// } => {
|
|
68
|
+
// const { store } = useStore({ store: options?.store })
|
|
69
|
+
// const fullKey = React.useMemo(
|
|
70
|
+
// // NOTE We're using the `makeQuery` function body string to make sure the key is unique across the app
|
|
71
|
+
// // TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
|
|
72
|
+
// // NOTE `makeQueryDef.toString()` doesn't work in Expo as it always produces `[native code]`
|
|
73
|
+
// () => (Array.isArray(key) ? key.join('-') : key) + '-' + store.reactivityGraph.id + '-' + makeQueryDef.toString(),
|
|
74
|
+
// [key, makeQueryDef, store.reactivityGraph.id],
|
|
75
|
+
// )
|
|
76
|
+
// const fullKeyRef = React.useRef<string | undefined>(undefined)
|
|
77
|
+
// const { query$, queryDef, otelContext } = React.useMemo(() => {
|
|
78
|
+
// if (fullKeyRef.current !== undefined && fullKeyRef.current !== fullKey) {
|
|
79
|
+
// // console.debug('fullKey changed', 'prev', fullKeyRef.current.split('-')[0]!, '-> new', fullKey.split('-')[0]!)
|
|
80
|
+
// const cachedItem = cache.get(fullKeyRef.current)
|
|
81
|
+
// if (cachedItem !== undefined && cachedItem._tag === 'active') {
|
|
82
|
+
// cachedItem.rc--
|
|
83
|
+
// if (cachedItem.rc === 0) {
|
|
84
|
+
// // console.debug('rc=0-changed', cachedItem.query$.id, cachedItem.query$.label)
|
|
85
|
+
// cachedItem.query$.destroy()
|
|
86
|
+
// cachedItem.span.end()
|
|
87
|
+
// cache.set(fullKeyRef.current, { _tag: 'destroyed' })
|
|
88
|
+
// }
|
|
89
|
+
// }
|
|
90
|
+
// }
|
|
91
|
+
// const cachedItem = cache.get(fullKey)
|
|
92
|
+
// if (cachedItem !== undefined && cachedItem._tag === 'active') {
|
|
93
|
+
// // console.debug('rc++', cachedItem.query$.id, cachedItem.query$.label)
|
|
94
|
+
// cachedItem.rc++
|
|
95
|
+
// return cachedItem
|
|
96
|
+
// }
|
|
97
|
+
// const spanName = options?.otel?.spanName ?? `LiveStore:useScopedQuery:${key}`
|
|
98
|
+
// const span = store.otel.tracer.startSpan(
|
|
99
|
+
// spanName,
|
|
100
|
+
// { attributes: options?.otel?.attributes },
|
|
101
|
+
// store.otel.queriesSpanContext,
|
|
102
|
+
// )
|
|
103
|
+
// const otelContext = otel.trace.setSpan(otel.context.active(), span)
|
|
104
|
+
// // console.debug('useScopedQuery:startSpan', fullKey, spanName)
|
|
105
|
+
// const queryDef = makeQueryDef(otelContext)
|
|
106
|
+
// // TODO keep a RC map of live queries
|
|
107
|
+
// const query$ = queryDef.make(store.reactivityGraph.context!)
|
|
108
|
+
// cache.set(fullKey, { _tag: 'active', rc: 1, query$, queryDef, span, otelContext })
|
|
109
|
+
// return { query$, queryDef, otelContext }
|
|
110
|
+
// // eslint-disable-next-line react-hooks/exhaustive-deps
|
|
111
|
+
// }, [fullKey])
|
|
112
|
+
// fullKeyRef.current = fullKey
|
|
113
|
+
// React.useEffect(() => {
|
|
114
|
+
// return () => {
|
|
115
|
+
// const fullKey = fullKeyRef.current!
|
|
116
|
+
// const cachedItem = cache.get(fullKey)
|
|
117
|
+
// // NOTE in case the fullKey changed then the query was already destroyed in the useMemo above
|
|
118
|
+
// if (cachedItem === undefined || cachedItem._tag === 'destroyed') return
|
|
119
|
+
// // console.debug('rc--', cachedItem.query$.id, cachedItem.query$.label)
|
|
120
|
+
// cachedItem.rc--
|
|
121
|
+
// if (cachedItem.rc === 0) {
|
|
122
|
+
// // console.debug('rc=0', cachedItem.query$.id, cachedItem.query$.label)
|
|
123
|
+
// cachedItem.query$.destroy()
|
|
124
|
+
// cachedItem.span.end()
|
|
125
|
+
// cache.delete(fullKey)
|
|
126
|
+
// }
|
|
127
|
+
// }
|
|
128
|
+
// }, [store.reactivityGraph])
|
|
129
|
+
// return { query$, queryDef, otelContext }
|
|
130
|
+
// }
|
|
87
131
|
//# sourceMappingURL=useScopedQuery.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScopedQuery.js","sourceRoot":"","sources":["../src/useScopedQuery.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAC1C,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,EAAE,QAAQ,EAAE,MAAM,uBAAuB,CAAA;AAChD,OAAO,EAAE,WAAW,EAAE,MAAM,eAAe,CAAA;
|
|
1
|
+
{"version":3,"file":"useScopedQuery.js","sourceRoot":"","sources":["../src/useScopedQuery.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import * as LiveStore from '@livestore/livestore';
|
|
2
2
|
import { queryDb } from '@livestore/livestore';
|
|
3
3
|
import { Effect, Schema } from '@livestore/utils/effect';
|
|
4
|
+
import { Vitest } from '@livestore/utils/node-vitest';
|
|
4
5
|
import { render, renderHook } from '@testing-library/react';
|
|
5
6
|
import React from 'react';
|
|
6
7
|
// @ts-expect-error no types
|
|
7
8
|
import * as ReactWindow from 'react-window';
|
|
8
|
-
import {
|
|
9
|
+
import { expect } from 'vitest';
|
|
9
10
|
import { makeTodoMvcReact, tables, todos } from './__tests__/fixture.js';
|
|
10
11
|
import * as LiveStoreReact from './mod.js';
|
|
11
|
-
describe('useScopedQuery', () => {
|
|
12
|
-
|
|
13
|
-
const { wrapper, store,
|
|
14
|
-
const renderCount = makeRenderCount();
|
|
12
|
+
Vitest.describe('useScopedQuery', () => {
|
|
13
|
+
Vitest.scopedLive('simple', () => Effect.gen(function* () {
|
|
14
|
+
const { wrapper, store, renderCount } = yield* makeTodoMvcReact();
|
|
15
15
|
store.mutate(todos.insert({ id: 't1', text: 'buy milk', completed: false }), todos.insert({ id: 't2', text: 'buy bread', completed: false }));
|
|
16
16
|
const queryMap = new Map();
|
|
17
|
+
const getRuns = (key) => store.reactivityGraph.context.liveQueryRCMap.get(queryMap.get(key).hash).query$.runs;
|
|
17
18
|
const { rerender, result, unmount } = renderHook((id) => {
|
|
18
19
|
renderCount.inc();
|
|
19
20
|
return LiveStoreReact.useScopedQuery(() => {
|
|
@@ -28,20 +29,20 @@ describe('useScopedQuery', () => {
|
|
|
28
29
|
expect(result.current.length).toBe(1);
|
|
29
30
|
expect(result.current[0].text).toBe('buy milk');
|
|
30
31
|
expect(renderCount.val).toBe(1);
|
|
31
|
-
expect(
|
|
32
|
+
expect(getRuns('t1')).toBe(1);
|
|
32
33
|
rerender('t2');
|
|
33
34
|
expect(result.current.length).toBe(1);
|
|
34
35
|
expect(result.current[0].text).toBe('buy bread');
|
|
35
36
|
expect(renderCount.val).toBe(2);
|
|
36
|
-
expect(
|
|
37
|
-
expect(
|
|
37
|
+
expect(getRuns('t1')).toBe(1);
|
|
38
|
+
expect(getRuns('t2')).toBe(1);
|
|
38
39
|
unmount();
|
|
39
|
-
expect(
|
|
40
|
-
})
|
|
40
|
+
expect(getRuns('t2')).toBe(1);
|
|
41
|
+
}));
|
|
41
42
|
// NOTE this test covers some special react lifecyle paths which I couldn't easily reproduce without react-window
|
|
42
43
|
// it basically causes a "query swap" in the `useMemo` and both a `useEffect` cleanup call.
|
|
43
44
|
// To handle this properly we introduced the `_tag: 'destroyed'` state in the `spanAlreadyStartedCache`.
|
|
44
|
-
|
|
45
|
+
Vitest.scopedLive('should work for a list with react-window', () => Effect.gen(function* () {
|
|
45
46
|
const { wrapper } = yield* makeTodoMvcReact();
|
|
46
47
|
const ListWrapper = ({ numItems }) => {
|
|
47
48
|
return (React.createElement(ReactWindow.FixedSizeList, { height: 100, width: 100, itemSize: 10, itemCount: numItems, itemData: Array.from({ length: numItems }, (_, i) => i).reverse() }, ListItem));
|
|
@@ -55,6 +56,6 @@ describe('useScopedQuery', () => {
|
|
|
55
56
|
expect(renderResult.container.textContent).toBe('0');
|
|
56
57
|
renderResult.rerender(React.createElement(ListWrapper, { numItems: 2 }));
|
|
57
58
|
expect(renderResult.container.textContent).toBe('10');
|
|
58
|
-
})
|
|
59
|
+
}));
|
|
59
60
|
});
|
|
60
61
|
//# sourceMappingURL=useScopedQuery.test.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useScopedQuery.test.js","sourceRoot":"","sources":["../src/useScopedQuery.test.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,SAAS,MAAM,sBAAsB,CAAA;AACjD,OAAO,EAAE,OAAO,EAAE,MAAM,sBAAsB,CAAA;AAC9C,OAAO,EAAE,MAAM,EAAE,MAAM,EAAE,MAAM,yBAAyB,CAAA;AACxD,OAAO,EAAE,MAAM,EAAE,UAAU,EAAE,MAAM,wBAAwB,CAAA;AAC3D,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,4BAA4B;AAC5B,OAAO,KAAK,WAAW,MAAM,cAAc,CAAA;AAC3C,OAAO,
|
|
1
|
+
{"version":3,"file":"useScopedQuery.test.js","sourceRoot":"","sources":["../src/useScopedQuery.test.tsx"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@livestore/react",
|
|
3
|
-
"version": "0.3.0-dev.
|
|
3
|
+
"version": "0.3.0-dev.12",
|
|
4
4
|
"type": "module",
|
|
5
5
|
"sideEffects": false,
|
|
6
6
|
"exports": {
|
|
@@ -23,10 +23,10 @@
|
|
|
23
23
|
},
|
|
24
24
|
"dependencies": {
|
|
25
25
|
"@opentelemetry/api": "1.9.0",
|
|
26
|
-
"@livestore/common": "0.3.0-dev.
|
|
27
|
-
"@livestore/livestore": "0.3.0-dev.
|
|
28
|
-
"@livestore/
|
|
29
|
-
"@livestore/
|
|
26
|
+
"@livestore/common": "0.3.0-dev.12",
|
|
27
|
+
"@livestore/livestore": "0.3.0-dev.12",
|
|
28
|
+
"@livestore/db-schema": "0.3.0-dev.12",
|
|
29
|
+
"@livestore/utils": "0.3.0-dev.12"
|
|
30
30
|
},
|
|
31
31
|
"devDependencies": {
|
|
32
32
|
"@opentelemetry/sdk-trace-base": "^1.30.1",
|
|
@@ -40,7 +40,7 @@
|
|
|
40
40
|
"typescript": "^5.7.2",
|
|
41
41
|
"vite": "^6.0.11",
|
|
42
42
|
"vitest": "^2.1.4",
|
|
43
|
-
"@livestore/web": "0.3.0-dev.
|
|
43
|
+
"@livestore/web": "0.3.0-dev.12"
|
|
44
44
|
},
|
|
45
45
|
"peerDependencies": {
|
|
46
46
|
"react": "~19.0.0"
|
package/src/LiveStoreContext.ts
CHANGED
|
@@ -1,11 +1,15 @@
|
|
|
1
|
-
import type { Store } from '@livestore/livestore'
|
|
2
|
-
import
|
|
3
|
-
import React, { useContext } from 'react'
|
|
1
|
+
import type { LiveStoreContextRunning, Store } from '@livestore/livestore'
|
|
2
|
+
import React from 'react'
|
|
4
3
|
|
|
5
|
-
export const LiveStoreContext = React.createContext<
|
|
4
|
+
export const LiveStoreContext = React.createContext<LiveStoreContextRunning | undefined>(undefined)
|
|
6
5
|
|
|
7
|
-
export const useStore = (): { store: Store } => {
|
|
8
|
-
|
|
6
|
+
export const useStore = (options?: { store?: Store }): { store: Store } => {
|
|
7
|
+
if (options?.store !== undefined) {
|
|
8
|
+
return { store: options.store }
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// eslint-disable-next-line react-hooks/rules-of-hooks
|
|
12
|
+
const storeContext = React.useContext(LiveStoreContext)
|
|
9
13
|
|
|
10
14
|
if (storeContext === undefined) {
|
|
11
15
|
throw new Error(`useStore can only be used inside StoreContext.Provider`)
|
|
@@ -1,4 +1,4 @@
|
|
|
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 {
|
|
@@ -35,7 +35,7 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
|
35
35
|
storeId?: string
|
|
36
36
|
boot?: (
|
|
37
37
|
store: Store<GraphQLContext, LiveStoreSchema>,
|
|
38
|
-
parentSpan: otel.Span,
|
|
38
|
+
ctx: { migrationsReport: MigrationsReport; parentSpan: otel.Span },
|
|
39
39
|
) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>
|
|
40
40
|
graphQLOptions?: GraphQLOptions<GraphQLContext>
|
|
41
41
|
otelOptions?: Partial<OtelOptions>
|
|
@@ -147,7 +147,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
147
147
|
adapter,
|
|
148
148
|
batchUpdates,
|
|
149
149
|
disableDevtools,
|
|
150
|
-
reactivityGraph,
|
|
151
150
|
signal,
|
|
152
151
|
}: CreateStoreOptions<GraphQLContext, LiveStoreSchema> & {
|
|
153
152
|
signal?: AbortSignal
|
|
@@ -176,7 +175,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
176
175
|
adapter,
|
|
177
176
|
batchUpdates,
|
|
178
177
|
disableDevtools,
|
|
179
|
-
reactivityGraph,
|
|
180
178
|
signal,
|
|
181
179
|
})
|
|
182
180
|
|
|
@@ -202,7 +200,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
202
200
|
inputPropsCacheRef.current.adapter !== adapter ||
|
|
203
201
|
inputPropsCacheRef.current.batchUpdates !== batchUpdates ||
|
|
204
202
|
inputPropsCacheRef.current.disableDevtools !== disableDevtools ||
|
|
205
|
-
inputPropsCacheRef.current.reactivityGraph !== reactivityGraph ||
|
|
206
203
|
inputPropsCacheRef.current.signal !== signal
|
|
207
204
|
) {
|
|
208
205
|
inputPropsCacheRef.current = {
|
|
@@ -213,7 +210,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
213
210
|
adapter,
|
|
214
211
|
batchUpdates,
|
|
215
212
|
disableDevtools,
|
|
216
|
-
reactivityGraph,
|
|
217
213
|
signal,
|
|
218
214
|
}
|
|
219
215
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
|
@@ -267,7 +263,6 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
267
263
|
graphQLOptions,
|
|
268
264
|
boot,
|
|
269
265
|
adapter,
|
|
270
|
-
reactivityGraph,
|
|
271
266
|
batchUpdates,
|
|
272
267
|
disableDevtools,
|
|
273
268
|
shutdownDeferred,
|
|
@@ -314,18 +309,7 @@ const useCreateStore = <GraphQLContext extends BaseGraphQLContext>({
|
|
|
314
309
|
ctxValueRef.current.shutdownDeferred = undefined
|
|
315
310
|
}
|
|
316
311
|
}
|
|
317
|
-
}, [
|
|
318
|
-
schema,
|
|
319
|
-
graphQLOptions,
|
|
320
|
-
otelOptions,
|
|
321
|
-
boot,
|
|
322
|
-
adapter,
|
|
323
|
-
batchUpdates,
|
|
324
|
-
disableDevtools,
|
|
325
|
-
signal,
|
|
326
|
-
reactivityGraph,
|
|
327
|
-
storeId,
|
|
328
|
-
])
|
|
312
|
+
}, [schema, graphQLOptions, otelOptions, boot, adapter, batchUpdates, disableDevtools, signal, storeId])
|
|
329
313
|
|
|
330
314
|
return ctxValueRef.current.value
|
|
331
315
|
}
|