@livestore/react 0.2.0 → 0.3.0-dev.11

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.
Files changed (87) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/LiveStoreContext.d.ts +5 -3
  3. package/dist/LiveStoreContext.d.ts.map +1 -1
  4. package/dist/LiveStoreContext.js +7 -3
  5. package/dist/LiveStoreContext.js.map +1 -1
  6. package/dist/LiveStoreProvider.d.ts +6 -4
  7. package/dist/LiveStoreProvider.d.ts.map +1 -1
  8. package/dist/LiveStoreProvider.js +47 -45
  9. package/dist/LiveStoreProvider.js.map +1 -1
  10. package/dist/LiveStoreProvider.test.js +8 -2
  11. package/dist/LiveStoreProvider.test.js.map +1 -1
  12. package/dist/__tests__/fixture.d.ts +7 -10
  13. package/dist/__tests__/fixture.d.ts.map +1 -1
  14. package/dist/__tests__/fixture.js +10 -15
  15. package/dist/__tests__/fixture.js.map +1 -1
  16. package/dist/experimental/components/LiveList.d.ts +2 -2
  17. package/dist/experimental/components/LiveList.d.ts.map +1 -1
  18. package/dist/experimental/components/LiveList.js +5 -4
  19. package/dist/experimental/components/LiveList.js.map +1 -1
  20. package/dist/mod.d.ts +0 -1
  21. package/dist/mod.d.ts.map +1 -1
  22. package/dist/mod.js +0 -1
  23. package/dist/mod.js.map +1 -1
  24. package/dist/useAtom.d.ts +4 -2
  25. package/dist/useAtom.d.ts.map +1 -1
  26. package/dist/useAtom.js +32 -28
  27. package/dist/useAtom.js.map +1 -1
  28. package/dist/useQuery.d.ts +26 -3
  29. package/dist/useQuery.d.ts.map +1 -1
  30. package/dist/useQuery.js +60 -45
  31. package/dist/useQuery.js.map +1 -1
  32. package/dist/useQuery.test.js +70 -16
  33. package/dist/useQuery.test.js.map +1 -1
  34. package/dist/useRcRef.d.ts +72 -0
  35. package/dist/useRcRef.d.ts.map +1 -0
  36. package/dist/useRcRef.js +146 -0
  37. package/dist/useRcRef.js.map +1 -0
  38. package/dist/useRcRef.test.d.ts +2 -0
  39. package/dist/useRcRef.test.d.ts.map +1 -0
  40. package/dist/useRcRef.test.js +128 -0
  41. package/dist/useRcRef.test.js.map +1 -0
  42. package/dist/useRcResource.d.ts +76 -0
  43. package/dist/useRcResource.d.ts.map +1 -0
  44. package/dist/useRcResource.js +150 -0
  45. package/dist/useRcResource.js.map +1 -0
  46. package/dist/useRcResource.test.d.ts +2 -0
  47. package/dist/useRcResource.test.d.ts.map +1 -0
  48. package/dist/useRcResource.test.js +122 -0
  49. package/dist/useRcResource.test.js.map +1 -0
  50. package/dist/useRow.d.ts +10 -7
  51. package/dist/useRow.d.ts.map +1 -1
  52. package/dist/useRow.js +16 -19
  53. package/dist/useRow.js.map +1 -1
  54. package/dist/useRow.test.js +74 -97
  55. package/dist/useRow.test.js.map +1 -1
  56. package/dist/useScopedQuery.d.ts +10 -4
  57. package/dist/useScopedQuery.d.ts.map +1 -1
  58. package/dist/useScopedQuery.js +97 -52
  59. package/dist/useScopedQuery.js.map +1 -1
  60. package/dist/useScopedQuery.test.js +13 -12
  61. package/dist/useScopedQuery.test.js.map +1 -1
  62. package/dist/utils/useStateRefWithReactiveInput.d.ts +1 -1
  63. package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
  64. package/dist/utils/useStateRefWithReactiveInput.js.map +1 -1
  65. package/package.json +18 -17
  66. package/src/LiveStoreContext.ts +10 -6
  67. package/src/LiveStoreProvider.test.tsx +13 -2
  68. package/src/LiveStoreProvider.tsx +69 -53
  69. package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
  70. package/src/__snapshots__/useRow.test.tsx.snap +347 -151
  71. package/src/__tests__/fixture.tsx +11 -19
  72. package/src/experimental/components/LiveList.tsx +8 -7
  73. package/src/mod.ts +0 -1
  74. package/src/useAtom.ts +22 -11
  75. package/src/useQuery.test.tsx +165 -67
  76. package/src/useQuery.ts +84 -54
  77. package/src/useRcResource.test.tsx +167 -0
  78. package/src/useRcResource.ts +180 -0
  79. package/src/useRow.test.tsx +130 -164
  80. package/src/useRow.ts +32 -35
  81. package/src/utils/useStateRefWithReactiveInput.ts +1 -1
  82. package/dist/useTemporaryQuery.d.ts +0 -22
  83. package/dist/useTemporaryQuery.d.ts.map +0 -1
  84. package/dist/useTemporaryQuery.js +0 -75
  85. package/dist/useTemporaryQuery.js.map +0 -1
  86. package/src/useScopedQuery.test.tsx +0 -96
  87. package/src/useScopedQuery.ts +0 -142
@@ -1,75 +0,0 @@
1
- import * as otel from '@opentelemetry/api';
2
- import React from 'react';
3
- import { useStore } from './LiveStoreContext.js';
4
- import { useQueryRef } from './useQuery.js';
5
- // NOTE Given `useMemo` will be called multiple times (e.g. when using React Strict mode or Fast Refresh),
6
- // we are using this cache to avoid starting multiple queries/spans for the same component.
7
- // This is somewhat against some recommended React best practices, but it should be fine in our case below.
8
- // Please definitely open an issue if you see or run into any problems with this approach!
9
- const cache = new Map();
10
- /**
11
- * Creates a query, subscribes and destroys it when the component unmounts.
12
- *
13
- * The `key` is used to determine whether the a new query should be created or if the existing one should be reused.
14
- */
15
- export const useScopedQuery = (makeQuery, key) => useScopedQueryRef(makeQuery, key).current;
16
- export const useScopedQueryRef = (makeQuery, key) => {
17
- const { query$ } = useMakeScopedQuery(makeQuery, key);
18
- return useQueryRef(query$);
19
- };
20
- export const useMakeScopedQuery = (makeQuery, key, options) => {
21
- const { store } = useStore();
22
- const fullKey = React.useMemo(
23
- // NOTE We're using the `makeQuery` function body string to make sure the key is unique across the app
24
- // TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
25
- () => (Array.isArray(key) ? key.join('-') : key) + '-' + store.reactivityGraph.id + '-' + makeQuery.toString(), [key, makeQuery, store.reactivityGraph.id]);
26
- const fullKeyRef = React.useRef();
27
- const { query$, otelContext } = React.useMemo(() => {
28
- if (fullKeyRef.current !== undefined && fullKeyRef.current !== fullKey) {
29
- // console.debug('fullKey changed', 'prev', fullKeyRef.current.split('-')[0]!, '-> new', fullKey.split('-')[0]!)
30
- const cachedItem = cache.get(fullKeyRef.current);
31
- if (cachedItem !== undefined && cachedItem._tag === 'active') {
32
- cachedItem.rc--;
33
- if (cachedItem.rc === 0) {
34
- // console.debug('rc=0-changed', cachedItem.query$.id, cachedItem.query$.label)
35
- cachedItem.query$.destroy();
36
- cachedItem.span.end();
37
- cache.set(fullKeyRef.current, { _tag: 'destroyed' });
38
- }
39
- }
40
- }
41
- const cachedItem = cache.get(fullKey);
42
- if (cachedItem !== undefined && cachedItem._tag === 'active') {
43
- // console.debug('rc++', cachedItem.query$.id, cachedItem.query$.label)
44
- cachedItem.rc++;
45
- return cachedItem;
46
- }
47
- const spanName = options?.otel?.spanName ?? `LiveStore:useScopedQuery:${key}`;
48
- const span = store.otel.tracer.startSpan(spanName, { attributes: options?.otel?.attributes }, store.otel.queriesSpanContext);
49
- const otelContext = otel.trace.setSpan(otel.context.active(), span);
50
- const query$ = makeQuery(otelContext);
51
- cache.set(fullKey, { _tag: 'active', rc: 1, query$, span, otelContext });
52
- return { query$, otelContext };
53
- // eslint-disable-next-line react-hooks/exhaustive-deps
54
- }, [fullKey]);
55
- fullKeyRef.current = fullKey;
56
- React.useEffect(() => {
57
- return () => {
58
- const fullKey = fullKeyRef.current;
59
- const cachedItem = cache.get(fullKey);
60
- // NOTE in case the fullKey changed then the query was already destroyed in the useMemo above
61
- if (cachedItem === undefined || cachedItem._tag === 'destroyed')
62
- return;
63
- // console.debug('rc--', cachedItem.query$.id, cachedItem.query$.label)
64
- cachedItem.rc--;
65
- if (cachedItem.rc === 0) {
66
- // console.debug('rc=0', cachedItem.query$.id, cachedItem.query$.label)
67
- cachedItem.query$.destroy();
68
- cachedItem.span.end();
69
- cache.delete(fullKey);
70
- }
71
- };
72
- }, []);
73
- return { query$, otelContext };
74
- };
75
- //# sourceMappingURL=useTemporaryQuery.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useTemporaryQuery.js","sourceRoot":"","sources":["../src/useTemporaryQuery.ts"],"names":[],"mappings":"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"}
@@ -1,96 +0,0 @@
1
- import * as LiveStore from '@livestore/livestore'
2
- import { queryDb } from '@livestore/livestore'
3
- import { Effect, Schema } from '@livestore/utils/effect'
4
- import { render, renderHook } from '@testing-library/react'
5
- import React from 'react'
6
- // @ts-expect-error no types
7
- import * as ReactWindow from 'react-window'
8
- import { describe, expect, it } from 'vitest'
9
-
10
- import { makeTodoMvcReact, tables, todos } from './__tests__/fixture.js'
11
- import * as LiveStoreReact from './mod.js'
12
-
13
- describe('useScopedQuery', () => {
14
- it('simple', () =>
15
- Effect.gen(function* () {
16
- const { wrapper, store, makeRenderCount } = yield* makeTodoMvcReact()
17
-
18
- const renderCount = makeRenderCount()
19
-
20
- store.mutate(
21
- todos.insert({ id: 't1', text: 'buy milk', completed: false }),
22
- todos.insert({ id: 't2', text: 'buy bread', completed: false }),
23
- )
24
-
25
- const queryMap = new Map<string, LiveStore.LiveQuery<any>>()
26
-
27
- const { rerender, result, unmount } = renderHook(
28
- (id: string) => {
29
- renderCount.inc()
30
-
31
- return LiveStoreReact.useScopedQuery(() => {
32
- const query$ = queryDb({
33
- query: `select * from todos where id = '${id}'`,
34
- schema: Schema.Array(tables.todos.schema),
35
- })
36
- queryMap.set(id, query$)
37
- return query$
38
- }, id)
39
- },
40
- { wrapper, initialProps: 't1' },
41
- )
42
-
43
- expect(result.current.length).toBe(1)
44
- expect(result.current[0]!.text).toBe('buy milk')
45
- expect(renderCount.val).toBe(1)
46
- expect(queryMap.get('t1')!.runs).toBe(1)
47
-
48
- rerender('t2')
49
-
50
- expect(result.current.length).toBe(1)
51
- expect(result.current[0]!.text).toBe('buy bread')
52
- expect(renderCount.val).toBe(2)
53
- expect(queryMap.get('t1')!.runs).toBe(1)
54
- expect(queryMap.get('t2')!.runs).toBe(1)
55
-
56
- unmount()
57
-
58
- expect(queryMap.get('t2')!.runs).toBe(1)
59
- }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
60
-
61
- // NOTE this test covers some special react lifecyle paths which I couldn't easily reproduce without react-window
62
- // it basically causes a "query swap" in the `useMemo` and both a `useEffect` cleanup call.
63
- // To handle this properly we introduced the `_tag: 'destroyed'` state in the `spanAlreadyStartedCache`.
64
- it('should work for a list with react-window', () =>
65
- Effect.gen(function* () {
66
- const { wrapper } = yield* makeTodoMvcReact()
67
-
68
- const ListWrapper: React.FC<{ numItems: number }> = ({ numItems }) => {
69
- return (
70
- <ReactWindow.FixedSizeList
71
- height={100}
72
- width={100}
73
- itemSize={10}
74
- itemCount={numItems}
75
- itemData={Array.from({ length: numItems }, (_, i) => i).reverse()}
76
- >
77
- {ListItem}
78
- </ReactWindow.FixedSizeList>
79
- )
80
- }
81
-
82
- const ListItem: React.FC<{ data: ReadonlyArray<number>; index: number }> = ({ data: ids, index }) => {
83
- const id = ids[index]!
84
- const res = LiveStoreReact.useScopedQuery(() => LiveStore.computed(() => id, { label: `ListItem.${id}` }), id)
85
- return <div role="listitem">{res}</div>
86
- }
87
-
88
- const renderResult = render(<ListWrapper numItems={1} />, { wrapper })
89
-
90
- expect(renderResult.container.textContent).toBe('0')
91
-
92
- renderResult.rerender(<ListWrapper numItems={2} />)
93
-
94
- expect(renderResult.container.textContent).toBe('10')
95
- }).pipe(Effect.scoped, Effect.tapCauseLogPretty, Effect.runPromise))
96
- })
@@ -1,142 +0,0 @@
1
- import type { QueryInfo } from '@livestore/common'
2
- import type { LiveQuery } from '@livestore/livestore'
3
- import * as otel from '@opentelemetry/api'
4
- import React from 'react'
5
-
6
- import { useStore } from './LiveStoreContext.js'
7
- import { useQueryRef } from './useQuery.js'
8
-
9
- // NOTE Given `useMemo` will be called multiple times (e.g. when using React Strict mode or Fast Refresh),
10
- // we are using this cache to avoid starting multiple queries/spans for the same component.
11
- // This is somewhat against some recommended React best practices, but it should be fine in our case below.
12
- // Please definitely open an issue if you see or run into any problems with this approach!
13
- const cache = new Map<
14
- string,
15
- | {
16
- _tag: 'active'
17
- rc: number
18
- query$: LiveQuery<any, any>
19
- span: otel.Span
20
- otelContext: otel.Context
21
- }
22
- | {
23
- _tag: 'destroyed'
24
- }
25
- >()
26
-
27
- export type DepKey = string | number | ReadonlyArray<string | number>
28
-
29
- /**
30
- * Creates a query, subscribes and destroys it when the component unmounts.
31
- *
32
- * The `key` is used to determine whether the a new query should be created or if the existing one should be reused.
33
- * This hook should be used instead of `useQuery` when the query should be dynamically created based on some props.
34
- * Otherwise when using `useQuery` the query will be leaked (i.e. never destroyed) when the component re-renders/unmounts.
35
- *
36
- * Example:
37
- * ```tsx
38
- * const issue = useScopedQuery(() => queryDb(tables.issues.query.where('id', issueId).first()), ['issue-details', issueId])
39
- * ```
40
- *
41
- * Important: On Expo/React Native please make sure the key contains a globally unique identifier, otherwise the query might get reused unintentionally.
42
- * Example: `['issue-details', issueId]`
43
- * See this issue to track progress: https://github.com/livestorejs/livestore/issues/231
44
- */
45
- export const useScopedQuery = <TResult>(makeQuery: () => LiveQuery<TResult>, key: DepKey): TResult =>
46
- useScopedQueryRef(makeQuery, key).current
47
-
48
- export const useScopedQueryRef = <TResult>(
49
- makeQuery: () => LiveQuery<TResult>,
50
- key: DepKey,
51
- ): React.MutableRefObject<TResult> => {
52
- const { query$ } = useMakeScopedQuery(makeQuery, key)
53
-
54
- return useQueryRef(query$)
55
- }
56
-
57
- export const useMakeScopedQuery = <TResult, TQueryInfo extends QueryInfo>(
58
- makeQuery: (otelContext: otel.Context) => LiveQuery<TResult, TQueryInfo>,
59
- key: DepKey,
60
- options?: {
61
- otel?: {
62
- spanName?: string
63
- attributes?: otel.Attributes
64
- }
65
- },
66
- ): { query$: LiveQuery<TResult, TQueryInfo>; otelContext: otel.Context } => {
67
- const { store } = useStore()
68
- const fullKey = React.useMemo(
69
- // NOTE We're using the `makeQuery` function body string to make sure the key is unique across the app
70
- // TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
71
- () => (Array.isArray(key) ? key.join('-') : key) + '-' + store.reactivityGraph.id + '-' + makeQuery.toString(),
72
- [key, makeQuery, store.reactivityGraph.id],
73
- )
74
- const fullKeyRef = React.useRef<string>()
75
-
76
- const { query$, otelContext } = React.useMemo(() => {
77
- if (fullKeyRef.current !== undefined && fullKeyRef.current !== fullKey) {
78
- // console.debug('fullKey changed', 'prev', fullKeyRef.current.split('-')[0]!, '-> new', fullKey.split('-')[0]!)
79
-
80
- const cachedItem = cache.get(fullKeyRef.current)
81
- if (cachedItem !== undefined && cachedItem._tag === 'active') {
82
- cachedItem.rc--
83
-
84
- if (cachedItem.rc === 0) {
85
- // console.debug('rc=0-changed', cachedItem.query$.id, cachedItem.query$.label)
86
- cachedItem.query$.destroy()
87
- cachedItem.span.end()
88
- cache.set(fullKeyRef.current, { _tag: 'destroyed' })
89
- }
90
- }
91
- }
92
-
93
- const cachedItem = cache.get(fullKey)
94
- if (cachedItem !== undefined && cachedItem._tag === 'active') {
95
- // console.debug('rc++', cachedItem.query$.id, cachedItem.query$.label)
96
- cachedItem.rc++
97
-
98
- return cachedItem
99
- }
100
-
101
- const spanName = options?.otel?.spanName ?? `LiveStore:useScopedQuery:${key}`
102
-
103
- const span = store.otel.tracer.startSpan(
104
- spanName,
105
- { attributes: options?.otel?.attributes },
106
- store.otel.queriesSpanContext,
107
- )
108
-
109
- const otelContext = otel.trace.setSpan(otel.context.active(), span)
110
-
111
- const query$ = makeQuery(otelContext)
112
-
113
- cache.set(fullKey, { _tag: 'active', rc: 1, query$, span, otelContext })
114
-
115
- return { query$, otelContext }
116
- // eslint-disable-next-line react-hooks/exhaustive-deps
117
- }, [fullKey])
118
-
119
- fullKeyRef.current = fullKey
120
-
121
- React.useEffect(() => {
122
- return () => {
123
- const fullKey = fullKeyRef.current!
124
- const cachedItem = cache.get(fullKey)
125
- // NOTE in case the fullKey changed then the query was already destroyed in the useMemo above
126
- if (cachedItem === undefined || cachedItem._tag === 'destroyed') return
127
-
128
- // console.debug('rc--', cachedItem.query$.id, cachedItem.query$.label)
129
-
130
- cachedItem.rc--
131
-
132
- if (cachedItem.rc === 0) {
133
- // console.debug('rc=0', cachedItem.query$.id, cachedItem.query$.label)
134
- cachedItem.query$.destroy()
135
- cachedItem.span.end()
136
- cache.delete(fullKey)
137
- }
138
- }
139
- }, [])
140
-
141
- return { query$, otelContext }
142
- }