@livestore/react 0.4.0-dev.2 → 0.4.0-dev.5

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.
@@ -60,6 +60,12 @@ const AppRouterSchema = State.SQLite.clientDocument({
60
60
  },
61
61
  })
62
62
 
63
+ const kv = State.SQLite.clientDocument({
64
+ name: 'Kv',
65
+ schema: Schema.Any,
66
+ default: { value: null },
67
+ })
68
+
63
69
  export const events = {
64
70
  todoCreated: Events.synced({
65
71
  name: 'todoCreated',
@@ -75,6 +81,7 @@ export const events = {
75
81
  }),
76
82
  AppRouterSet: AppRouterSchema.set,
77
83
  UserInfoSet: userInfo.set,
84
+ KvSet: kv.set,
78
85
  }
79
86
 
80
87
  const materializers = State.SQLite.materializers(events, {
@@ -82,7 +89,7 @@ const materializers = State.SQLite.materializers(events, {
82
89
  todoUpdated: ({ id, text, completed }) => todos.update({ completed, text }).where({ id }),
83
90
  })
84
91
 
85
- export const tables = { todos, app, userInfo, AppRouterSchema }
92
+ export const tables = { todos, app, userInfo, AppRouterSchema, kv }
86
93
 
87
94
  const state = State.SQLite.makeState({ tables, materializers })
88
95
  export const schema = makeSchema({ state, events })
package/src/mod.ts CHANGED
@@ -3,8 +3,9 @@ export { LiveStoreProvider } from './LiveStoreProvider.tsx'
3
3
  export {
4
4
  type Dispatch,
5
5
  type SetStateAction,
6
+ type SetStateActionPartial,
6
7
  type StateSetters,
7
- type UseRowResult as UseStateResult,
8
+ type UseClientDocumentResult,
8
9
  useClientDocument,
9
10
  } from './useClientDocument.ts'
10
11
  export { useQuery, useQueryRef } from './useQuery.ts'
@@ -1,7 +1,7 @@
1
1
  /** biome-ignore-all lint/a11y/useValidAriaRole: not needed for testing */
2
2
  /** biome-ignore-all lint/a11y/noStaticElementInteractions: not needed for testing */
3
3
  import * as LiveStore from '@livestore/livestore'
4
- import { getSimplifiedRootSpan } from '@livestore/livestore/internal/testing-utils'
4
+ import { getAllSimplifiedRootSpans, getSimplifiedRootSpan } from '@livestore/livestore/internal/testing-utils'
5
5
  import { Effect, ReadonlyRecord, Schema } from '@livestore/utils/effect'
6
6
  import { Vitest } from '@livestore/utils-dev/node-vitest'
7
7
  import * as otel from '@opentelemetry/api'
@@ -224,6 +224,34 @@ Vitest.describe('useClientDocument', () => {
224
224
  }),
225
225
  )
226
226
 
227
+ Vitest.scopedLive('kv client document overwrites value (Schema.Any, no partial merge)', () =>
228
+ Effect.gen(function* () {
229
+ const { wrapper, store, renderCount } = yield* makeTodoMvcReact({})
230
+
231
+ const { result } = ReactTesting.renderHook(
232
+ (id: string) => {
233
+ renderCount.inc()
234
+
235
+ const [state, setState] = store.useClientDocument(tables.kv, id)
236
+ return { state, setState, id }
237
+ },
238
+ { wrapper, initialProps: 'k1' },
239
+ )
240
+
241
+ expect(result.current.id).toBe('k1')
242
+ expect(result.current.state).toBe(null)
243
+ expect(renderCount.val).toBe(1)
244
+
245
+ ReactTesting.act(() => result.current.setState(1))
246
+ expect(result.current.state).toEqual(1)
247
+ expect(renderCount.val).toBe(2)
248
+
249
+ ReactTesting.act(() => result.current.setState({ b: 2 }))
250
+ expect(result.current.state).toEqual({ b: 2 })
251
+ expect(renderCount.val).toBe(3)
252
+ }),
253
+ )
254
+
227
255
  Vitest.describe('otel', () => {
228
256
  it.each([{ strictMode: true }, { strictMode: false }])(
229
257
  'should update the data based on component key strictMode=%s',
@@ -295,7 +323,8 @@ Vitest.describe('useClientDocument', () => {
295
323
  })
296
324
  }
297
325
 
298
- expect(getSimplifiedRootSpan(exporter, mapAttributes)).toMatchSnapshot()
326
+ expect(getSimplifiedRootSpan(exporter, 'createStore', mapAttributes)).toMatchSnapshot()
327
+ expect(getAllSimplifiedRootSpans(exporter, 'LiveStore:commit', mapAttributes)).toMatchSnapshot()
299
328
 
300
329
  await provider.shutdown()
301
330
  },
@@ -9,7 +9,7 @@ import React from 'react'
9
9
  import { LiveStoreContext } from './LiveStoreContext.ts'
10
10
  import { useQueryRef } from './useQuery.ts'
11
11
 
12
- export type UseRowResult<TTableDef extends State.SQLite.ClientDocumentTableDef.TraitAny> = [
12
+ export type UseClientDocumentResult<TTableDef extends State.SQLite.ClientDocumentTableDef.TraitAny> = [
13
13
  row: TTableDef['Value'],
14
14
  setRow: StateSetters<TTableDef>,
15
15
  id: string,
@@ -54,13 +54,17 @@ export const useClientDocument: {
54
54
  any,
55
55
  any,
56
56
  any,
57
- { partialSet: boolean; default: { id: string | SessionIdSymbol; value: any } }
57
+ {
58
+ partialSet: boolean
59
+ /** Default value to use instead of the default value from the table definition */
60
+ default: any
61
+ }
58
62
  >,
59
63
  >(
60
64
  table: TTableDef,
61
65
  id?: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | SessionIdSymbol,
62
66
  options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>,
63
- ): UseRowResult<TTableDef>
67
+ ): UseClientDocumentResult<TTableDef>
64
68
 
65
69
  // case: no default id → id arg is required
66
70
  <
@@ -68,20 +72,24 @@ export const useClientDocument: {
68
72
  any,
69
73
  any,
70
74
  any,
71
- { partialSet: boolean; default: { id: string | SessionIdSymbol | undefined; value: any } }
75
+ {
76
+ partialSet: boolean
77
+ /** Default value to use instead of the default value from the table definition */
78
+ default: any
79
+ }
72
80
  >,
73
81
  >(
74
82
  table: TTableDef,
75
83
  // TODO adjust so it works with arbitrary primary keys or unique constraints
76
84
  id: State.SQLite.ClientDocumentTableDef.DefaultIdType<TTableDef> | string | SessionIdSymbol,
77
85
  options?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>,
78
- ): UseRowResult<TTableDef>
86
+ ): UseClientDocumentResult<TTableDef>
79
87
  } = <TTableDef extends State.SQLite.ClientDocumentTableDef.Any>(
80
88
  table: TTableDef,
81
89
  idOrOptions?: string | SessionIdSymbol,
82
90
  options_?: Partial<RowQuery.GetOrCreateOptions<TTableDef>>,
83
91
  storeArg?: { store?: Store },
84
- ): UseRowResult<TTableDef> => {
92
+ ): UseClientDocumentResult<TTableDef> => {
85
93
  const id =
86
94
  typeof idOrOptions === 'string' || idOrOptions === SessionIdSymbol
87
95
  ? idOrOptions
@@ -97,8 +105,7 @@ export const useClientDocument: {
97
105
  const tableName = table.sqliteDef.name
98
106
 
99
107
  const store =
100
- storeArg?.store ??
101
- // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
108
+ storeArg?.store ?? // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
102
109
  React.useContext(LiveStoreContext)?.store ??
103
110
  shouldNeverHappen(`No store provided to useClientDocument`)
104
111
 
@@ -134,10 +141,13 @@ export const useClientDocument: {
134
141
  }
135
142
 
136
143
  export type Dispatch<A> = (action: A) => void
137
- export type SetStateAction<S> = Partial<S> | ((previousValue: S) => Partial<S>)
144
+ export type SetStateActionPartial<S> = Partial<S> | ((previousValue: S) => Partial<S>)
145
+ export type SetStateAction<S> = S | ((previousValue: S) => S)
138
146
 
139
147
  export type StateSetters<TTableDef extends State.SQLite.ClientDocumentTableDef.TraitAny> = Dispatch<
140
- SetStateAction<TTableDef['Value']>
148
+ TTableDef[State.SQLite.ClientDocumentTableDefSymbol]['options']['partialSet'] extends false
149
+ ? SetStateAction<TTableDef['Value']>
150
+ : SetStateActionPartial<TTableDef['Value']>
141
151
  >
142
152
 
143
153
  const validateTableOptions = (table: State.SQLite.TableDef<any, any>) => {
package/src/useQuery.ts CHANGED
@@ -42,8 +42,7 @@ export const useQueryRef = <TQuery extends LiveQueryDef.Any>(
42
42
  queryRcRef: LiveQueries.RcRef<LiveQuery<LiveQueries.GetResult<TQuery>>>
43
43
  } => {
44
44
  const store =
45
- options?.store ??
46
- // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
45
+ options?.store ?? // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
47
46
  React.useContext(LiveStoreContext)?.store ??
48
47
  shouldNeverHappen(`No store provided to useQuery`)
49
48