@livestore/react 0.3.0-dev.9 → 0.3.1-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/LICENSE +201 -0
- package/dist/.tsbuildinfo +1 -1
- package/dist/LiveStoreContext.d.ts +10 -4
- package/dist/LiveStoreContext.d.ts.map +1 -1
- package/dist/LiveStoreContext.js +1 -11
- package/dist/LiveStoreContext.js.map +1 -1
- package/dist/LiveStoreProvider.d.ts +29 -12
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +84 -55
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/LiveStoreProvider.test.js +80 -29
- package/dist/LiveStoreProvider.test.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +122 -556
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +71 -30
- 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 +10 -6
- package/dist/experimental/components/LiveList.js.map +1 -1
- package/dist/mod.d.ts +4 -5
- package/dist/mod.d.ts.map +1 -1
- package/dist/mod.js +4 -5
- package/dist/mod.js.map +1 -1
- package/dist/useClientDocument.d.ts +61 -0
- package/dist/useClientDocument.d.ts.map +1 -0
- package/dist/useClientDocument.js +79 -0
- package/dist/useClientDocument.js.map +1 -0
- package/dist/useClientDocument.test.d.ts +2 -0
- package/dist/useClientDocument.test.d.ts.map +1 -0
- package/dist/useClientDocument.test.js +175 -0
- package/dist/useClientDocument.test.js.map +1 -0
- package/dist/useQuery.d.ts +25 -3
- package/dist/useQuery.d.ts.map +1 -1
- package/dist/useQuery.js +67 -47
- package/dist/useQuery.js.map +1 -1
- package/dist/useQuery.test.d.ts +1 -1
- package/dist/useQuery.test.d.ts.map +1 -1
- package/dist/useQuery.test.js +86 -24
- package/dist/useQuery.test.js.map +1 -1
- package/dist/useRcResource.d.ts +76 -0
- package/dist/useRcResource.d.ts.map +1 -0
- package/dist/useRcResource.js +152 -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/useStore.d.ts +9 -0
- package/dist/useStore.d.ts.map +1 -0
- package/dist/useStore.js +28 -0
- package/dist/useStore.js.map +1 -0
- package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
- package/package.json +20 -13
- package/src/LiveStoreContext.ts +11 -16
- package/src/LiveStoreProvider.test.tsx +176 -37
- package/src/LiveStoreProvider.tsx +156 -81
- package/src/__snapshots__/useClientDocument.test.tsx.snap +613 -0
- package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
- package/src/__tests__/fixture.tsx +74 -47
- package/src/experimental/components/LiveList.tsx +10 -7
- package/src/mod.ts +5 -6
- package/src/useClientDocument.test.tsx +306 -0
- package/src/useClientDocument.ts +157 -0
- package/src/useQuery.test.tsx +182 -71
- package/src/useQuery.ts +95 -58
- package/src/useRcResource.test.tsx +167 -0
- package/src/useRcResource.ts +182 -0
- package/src/useStore.ts +36 -0
- package/dist/useAtom.d.ts +0 -5
- package/dist/useAtom.d.ts.map +0 -1
- package/dist/useAtom.js +0 -38
- package/dist/useAtom.js.map +0 -1
- package/dist/useRow.d.ts +0 -50
- package/dist/useRow.d.ts.map +0 -1
- package/dist/useRow.js +0 -93
- package/dist/useRow.js.map +0 -1
- package/dist/useRow.test.d.ts +0 -2
- package/dist/useRow.test.d.ts.map +0 -1
- package/dist/useRow.test.js +0 -202
- package/dist/useRow.test.js.map +0 -1
- package/dist/useScopedQuery.d.ts +0 -33
- package/dist/useScopedQuery.d.ts.map +0 -1
- package/dist/useScopedQuery.js +0 -87
- package/dist/useScopedQuery.js.map +0 -1
- package/dist/useScopedQuery.test.d.ts +0 -2
- package/dist/useScopedQuery.test.d.ts.map +0 -1
- package/dist/useScopedQuery.test.js +0 -60
- package/dist/useScopedQuery.test.js.map +0 -1
- package/src/__snapshots__/useRow.test.tsx.snap +0 -360
- package/src/useAtom.ts +0 -52
- package/src/useRow.test.tsx +0 -344
- package/src/useRow.ts +0 -188
- package/src/useScopedQuery.test.tsx +0 -96
- package/src/useScopedQuery.ts +0 -143
- package/tsconfig.json +0 -20
- package/vitest.config.js +0 -17
@@ -1,27 +1,30 @@
|
|
1
|
+
import { makeInMemoryAdapter } from '@livestore/adapter-web'
|
1
2
|
import { sql } from '@livestore/common'
|
2
|
-
import {
|
3
|
+
import { rawSqlEvent } from '@livestore/common/schema'
|
3
4
|
import { queryDb, type Store } from '@livestore/livestore'
|
4
5
|
import { Schema } from '@livestore/utils/effect'
|
5
|
-
import
|
6
|
-
import { render, screen, waitFor, waitForElementToBeRemoved } from '@testing-library/react'
|
6
|
+
import * as ReactTesting from '@testing-library/react'
|
7
7
|
import React from 'react'
|
8
8
|
import { unstable_batchedUpdates as batchUpdates } from 'react-dom'
|
9
9
|
import { describe, expect, it } from 'vitest'
|
10
10
|
|
11
|
-
import { schema, tables } from './__tests__/fixture.js'
|
11
|
+
import { events, schema, tables } from './__tests__/fixture.js'
|
12
12
|
import { LiveStoreProvider } from './LiveStoreProvider.js'
|
13
13
|
import * as LiveStoreReact from './mod.js'
|
14
14
|
|
15
|
-
describe('LiveStoreProvider', () => {
|
15
|
+
describe.each([true, false])('LiveStoreProvider (strictMode: %s)', (strictMode) => {
|
16
|
+
const WithStrictMode = strictMode ? React.StrictMode : React.Fragment
|
17
|
+
|
16
18
|
it('simple', async () => {
|
17
19
|
let appRenderCount = 0
|
18
20
|
|
19
|
-
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.
|
21
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) })
|
20
22
|
|
21
23
|
const App = () => {
|
22
24
|
appRenderCount++
|
25
|
+
const { store } = LiveStoreReact.useStore()
|
23
26
|
|
24
|
-
const todos =
|
27
|
+
const todos = store.useQuery(allTodos$)
|
25
28
|
|
26
29
|
return <div>{JSON.stringify(todos)}</div>
|
27
30
|
}
|
@@ -31,8 +34,8 @@ describe('LiveStoreProvider', () => {
|
|
31
34
|
const Root = ({ forceUpdate }: { forceUpdate: number }) => {
|
32
35
|
const bootCb = React.useCallback(
|
33
36
|
(store: Store) =>
|
34
|
-
store.
|
35
|
-
|
37
|
+
store.commit(
|
38
|
+
rawSqlEvent({
|
36
39
|
sql: sql`INSERT OR IGNORE INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
|
37
40
|
}),
|
38
41
|
),
|
@@ -41,37 +44,43 @@ describe('LiveStoreProvider', () => {
|
|
41
44
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
42
45
|
const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate])
|
43
46
|
return (
|
44
|
-
<
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
47
|
+
<WithStrictMode>
|
48
|
+
<LiveStoreProvider
|
49
|
+
schema={schema}
|
50
|
+
renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
|
51
|
+
adapter={adapterMemo}
|
52
|
+
boot={bootCb}
|
53
|
+
signal={abortController.signal}
|
54
|
+
batchUpdates={batchUpdates}
|
55
|
+
>
|
56
|
+
<App />
|
57
|
+
</LiveStoreProvider>
|
58
|
+
</WithStrictMode>
|
54
59
|
)
|
55
60
|
}
|
56
61
|
|
57
|
-
const { rerender } = render(<Root forceUpdate={1} />)
|
62
|
+
const { rerender } = ReactTesting.render(<Root forceUpdate={1} />)
|
58
63
|
|
59
64
|
expect(appRenderCount).toBe(0)
|
60
65
|
|
61
|
-
await waitForElementToBeRemoved(() =>
|
66
|
+
await ReactTesting.waitForElementToBeRemoved(() =>
|
67
|
+
ReactTesting.screen.getByText((_) => _.startsWith('Loading LiveStore')),
|
68
|
+
)
|
62
69
|
|
63
|
-
expect(appRenderCount).toBe(1)
|
70
|
+
expect(appRenderCount).toBe(strictMode ? 2 : 1)
|
64
71
|
|
65
72
|
rerender(<Root forceUpdate={2} />)
|
66
73
|
|
67
|
-
await waitFor(() => screen.getByText('Loading LiveStore: loading'))
|
68
|
-
await waitFor(() => screen.getByText((_) => _.includes('buy milk')))
|
74
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('Loading LiveStore: loading'))
|
75
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText((_) => _.includes('buy milk')))
|
69
76
|
|
70
|
-
expect(appRenderCount).toBe(2)
|
77
|
+
expect(appRenderCount).toBe(strictMode ? 4 : 2)
|
71
78
|
|
72
79
|
abortController.abort()
|
73
80
|
|
74
|
-
await waitFor(() =>
|
81
|
+
await ReactTesting.waitFor(() =>
|
82
|
+
ReactTesting.screen.getByText('LiveStore Shutdown due to interrupted', { exact: false }),
|
83
|
+
)
|
75
84
|
})
|
76
85
|
|
77
86
|
// TODO test aborting during boot
|
@@ -88,8 +97,8 @@ describe('LiveStoreProvider', () => {
|
|
88
97
|
const Root = ({ forceUpdate }: { forceUpdate: number }) => {
|
89
98
|
const bootCb = React.useCallback(
|
90
99
|
(store: Store) =>
|
91
|
-
store.
|
92
|
-
|
100
|
+
store.commit(
|
101
|
+
rawSqlEvent({
|
93
102
|
sql: sql`INSERT OR IGNORE INTO todos_missing_table (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
|
94
103
|
}),
|
95
104
|
),
|
@@ -98,22 +107,152 @@ describe('LiveStoreProvider', () => {
|
|
98
107
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
99
108
|
const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate])
|
100
109
|
return (
|
110
|
+
<WithStrictMode>
|
111
|
+
<LiveStoreProvider
|
112
|
+
schema={schema}
|
113
|
+
renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
|
114
|
+
adapter={adapterMemo}
|
115
|
+
boot={bootCb}
|
116
|
+
batchUpdates={batchUpdates}
|
117
|
+
>
|
118
|
+
<App />
|
119
|
+
</LiveStoreProvider>
|
120
|
+
</WithStrictMode>
|
121
|
+
)
|
122
|
+
}
|
123
|
+
|
124
|
+
ReactTesting.render(<Root forceUpdate={1} />)
|
125
|
+
|
126
|
+
expect(appRenderCount).toBe(0)
|
127
|
+
|
128
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText((_) => _.startsWith('LiveStore.UnexpectedError')))
|
129
|
+
})
|
130
|
+
|
131
|
+
it('unmounts when store is shutdown', async () => {
|
132
|
+
let appRenderCount = 0
|
133
|
+
|
134
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) })
|
135
|
+
|
136
|
+
const shutdownDeferred = Promise.withResolvers<void>()
|
137
|
+
|
138
|
+
const App = () => {
|
139
|
+
appRenderCount++
|
140
|
+
const { store } = LiveStoreReact.useStore()
|
141
|
+
|
142
|
+
React.useEffect(() => {
|
143
|
+
shutdownDeferred.promise.then(() => {
|
144
|
+
console.log('shutdown')
|
145
|
+
return store.shutdown()
|
146
|
+
})
|
147
|
+
}, [store])
|
148
|
+
|
149
|
+
const todos = store.useQuery(allTodos$)
|
150
|
+
|
151
|
+
return <div>{JSON.stringify(todos)}</div>
|
152
|
+
}
|
153
|
+
|
154
|
+
const adapter = makeInMemoryAdapter()
|
155
|
+
|
156
|
+
const Root = () => {
|
157
|
+
return (
|
158
|
+
<WithStrictMode>
|
159
|
+
<LiveStoreProvider
|
160
|
+
schema={schema}
|
161
|
+
renderLoading={(status) => <div>Loading LiveStore: {status.stage}</div>}
|
162
|
+
adapter={adapter}
|
163
|
+
batchUpdates={batchUpdates}
|
164
|
+
>
|
165
|
+
<App />
|
166
|
+
</LiveStoreProvider>
|
167
|
+
</WithStrictMode>
|
168
|
+
)
|
169
|
+
}
|
170
|
+
|
171
|
+
ReactTesting.render(<Root />)
|
172
|
+
|
173
|
+
expect(appRenderCount).toBe(0)
|
174
|
+
|
175
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('[]'))
|
176
|
+
|
177
|
+
React.act(() => shutdownDeferred.resolve())
|
178
|
+
|
179
|
+
expect(appRenderCount).toBe(strictMode ? 2 : 1)
|
180
|
+
|
181
|
+
await ReactTesting.waitFor(() =>
|
182
|
+
ReactTesting.screen.getByText('LiveStore Shutdown due to manual shutdown', { exact: false }),
|
183
|
+
)
|
184
|
+
})
|
185
|
+
})
|
186
|
+
|
187
|
+
it('should work two stores with the same storeId', async () => {
|
188
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) })
|
189
|
+
|
190
|
+
const appRenderCount = {
|
191
|
+
store1: 0,
|
192
|
+
store2: 0,
|
193
|
+
}
|
194
|
+
|
195
|
+
const App = () => {
|
196
|
+
const { store } = LiveStoreReact.useStore()
|
197
|
+
const instanceId = store.clientSession.debugInstanceId as 'store1' | 'store2'
|
198
|
+
appRenderCount[instanceId]!++
|
199
|
+
|
200
|
+
const todos = store.useQuery(allTodos$)
|
201
|
+
|
202
|
+
return (
|
203
|
+
<div id={instanceId}>
|
204
|
+
<div role="heading">{instanceId}</div>
|
205
|
+
<div role="content">{JSON.stringify(todos)}</div>
|
206
|
+
<button onClick={() => store.commit(events.todoCreated({ id: 't1', text: 'buy milk', completed: false }))}>
|
207
|
+
create todo {instanceId}
|
208
|
+
</button>
|
209
|
+
</div>
|
210
|
+
)
|
211
|
+
}
|
212
|
+
|
213
|
+
const Root = () => {
|
214
|
+
const storeId = 'fixed-store-id'
|
215
|
+
return (
|
216
|
+
<div>
|
101
217
|
<LiveStoreProvider
|
218
|
+
storeId={storeId}
|
219
|
+
debug={{ instanceId: 'store1' }}
|
102
220
|
schema={schema}
|
103
|
-
|
104
|
-
adapter={adapterMemo}
|
105
|
-
boot={bootCb}
|
221
|
+
adapter={makeInMemoryAdapter()}
|
106
222
|
batchUpdates={batchUpdates}
|
107
223
|
>
|
108
224
|
<App />
|
109
225
|
</LiveStoreProvider>
|
110
|
-
|
111
|
-
|
226
|
+
<LiveStoreProvider
|
227
|
+
storeId={storeId}
|
228
|
+
debug={{ instanceId: 'store2' }}
|
229
|
+
schema={schema}
|
230
|
+
adapter={makeInMemoryAdapter()}
|
231
|
+
batchUpdates={batchUpdates}
|
232
|
+
>
|
233
|
+
<App />
|
234
|
+
</LiveStoreProvider>
|
235
|
+
</div>
|
236
|
+
)
|
237
|
+
}
|
112
238
|
|
113
|
-
|
239
|
+
const { container } = ReactTesting.render(<Root />)
|
114
240
|
|
115
|
-
|
241
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByRole('heading', { name: 'store1' }))
|
242
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByRole('heading', { name: 'store2' }))
|
116
243
|
|
117
|
-
|
118
|
-
|
244
|
+
expect(appRenderCount.store1).toBe(1)
|
245
|
+
expect(appRenderCount.store2).toBe(1)
|
246
|
+
|
247
|
+
ReactTesting.fireEvent.click(ReactTesting.screen.getByText('create todo store1'))
|
248
|
+
|
249
|
+
expect(appRenderCount.store1).toBe(2)
|
250
|
+
|
251
|
+
expect(container.querySelector('#store1 > div[role="content"]')?.textContent).toBe(
|
252
|
+
'[{"id":"t1","text":"buy milk","completed":false}]',
|
253
|
+
)
|
254
|
+
|
255
|
+
expect(container.querySelector('#store2 > div[role="content"]')?.textContent).toBe('[]')
|
119
256
|
})
|
257
|
+
|
258
|
+
// TODO test that checks that there are no two exact same instances (i.e. same storeId, clientId, sessionId)
|