@livestore/react 0.4.0-dev.21 → 0.4.0-dev.22

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 (97) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/StoreRegistryContext.d.ts +56 -0
  3. package/dist/StoreRegistryContext.d.ts.map +1 -0
  4. package/dist/StoreRegistryContext.js +61 -0
  5. package/dist/StoreRegistryContext.js.map +1 -0
  6. package/dist/__tests__/fixture.d.ts.map +1 -1
  7. package/dist/__tests__/fixture.js +1 -6
  8. package/dist/__tests__/fixture.js.map +1 -1
  9. package/dist/experimental/components/LiveList.d.ts +4 -2
  10. package/dist/experimental/components/LiveList.d.ts.map +1 -1
  11. package/dist/experimental/components/LiveList.js +6 -5
  12. package/dist/experimental/components/LiveList.js.map +1 -1
  13. package/dist/experimental/mod.d.ts +0 -1
  14. package/dist/experimental/mod.d.ts.map +1 -1
  15. package/dist/experimental/mod.js +0 -1
  16. package/dist/experimental/mod.js.map +1 -1
  17. package/dist/mod.d.ts +4 -3
  18. package/dist/mod.d.ts.map +1 -1
  19. package/dist/mod.js +3 -2
  20. package/dist/mod.js.map +1 -1
  21. package/dist/useClientDocument.d.ts.map +1 -1
  22. package/dist/useClientDocument.js +1 -4
  23. package/dist/useClientDocument.js.map +1 -1
  24. package/dist/useQuery.d.ts +1 -1
  25. package/dist/useQuery.d.ts.map +1 -1
  26. package/dist/useQuery.js +2 -5
  27. package/dist/useQuery.js.map +1 -1
  28. package/dist/useStore.d.ts +50 -46
  29. package/dist/useStore.d.ts.map +1 -1
  30. package/dist/useStore.js +66 -59
  31. package/dist/useStore.js.map +1 -1
  32. package/dist/useStore.test.d.ts.map +1 -0
  33. package/dist/{experimental/multi-store/useStore.test.js → useStore.test.js} +20 -22
  34. package/dist/useStore.test.js.map +1 -0
  35. package/package.json +7 -7
  36. package/src/StoreRegistryContext.tsx +69 -0
  37. package/src/__tests__/fixture.tsx +1 -13
  38. package/src/experimental/components/LiveList.tsx +13 -4
  39. package/src/experimental/mod.ts +0 -1
  40. package/src/mod.ts +4 -3
  41. package/src/useClientDocument.ts +1 -5
  42. package/src/useQuery.ts +2 -6
  43. package/src/{experimental/multi-store/useStore.test.tsx → useStore.test.tsx} +32 -30
  44. package/src/useStore.ts +94 -66
  45. package/dist/LiveStoreContext.d.ts +0 -40
  46. package/dist/LiveStoreContext.d.ts.map +0 -1
  47. package/dist/LiveStoreContext.js +0 -21
  48. package/dist/LiveStoreContext.js.map +0 -1
  49. package/dist/LiveStoreProvider.d.ts +0 -73
  50. package/dist/LiveStoreProvider.d.ts.map +0 -1
  51. package/dist/LiveStoreProvider.js +0 -233
  52. package/dist/LiveStoreProvider.js.map +0 -1
  53. package/dist/LiveStoreProvider.test.d.ts +0 -2
  54. package/dist/LiveStoreProvider.test.d.ts.map +0 -1
  55. package/dist/LiveStoreProvider.test.js +0 -117
  56. package/dist/LiveStoreProvider.test.js.map +0 -1
  57. package/dist/experimental/multi-store/StoreRegistry.d.ts +0 -105
  58. package/dist/experimental/multi-store/StoreRegistry.d.ts.map +0 -1
  59. package/dist/experimental/multi-store/StoreRegistry.js +0 -184
  60. package/dist/experimental/multi-store/StoreRegistry.js.map +0 -1
  61. package/dist/experimental/multi-store/StoreRegistry.test.d.ts +0 -2
  62. package/dist/experimental/multi-store/StoreRegistry.test.d.ts.map +0 -1
  63. package/dist/experimental/multi-store/StoreRegistry.test.js +0 -381
  64. package/dist/experimental/multi-store/StoreRegistry.test.js.map +0 -1
  65. package/dist/experimental/multi-store/StoreRegistryContext.d.ts +0 -10
  66. package/dist/experimental/multi-store/StoreRegistryContext.d.ts.map +0 -1
  67. package/dist/experimental/multi-store/StoreRegistryContext.js +0 -15
  68. package/dist/experimental/multi-store/StoreRegistryContext.js.map +0 -1
  69. package/dist/experimental/multi-store/mod.d.ts +0 -6
  70. package/dist/experimental/multi-store/mod.d.ts.map +0 -1
  71. package/dist/experimental/multi-store/mod.js +0 -6
  72. package/dist/experimental/multi-store/mod.js.map +0 -1
  73. package/dist/experimental/multi-store/storeOptions.d.ts +0 -4
  74. package/dist/experimental/multi-store/storeOptions.d.ts.map +0 -1
  75. package/dist/experimental/multi-store/storeOptions.js +0 -4
  76. package/dist/experimental/multi-store/storeOptions.js.map +0 -1
  77. package/dist/experimental/multi-store/types.d.ts +0 -25
  78. package/dist/experimental/multi-store/types.d.ts.map +0 -1
  79. package/dist/experimental/multi-store/types.js +0 -2
  80. package/dist/experimental/multi-store/types.js.map +0 -1
  81. package/dist/experimental/multi-store/useStore.d.ts +0 -11
  82. package/dist/experimental/multi-store/useStore.d.ts.map +0 -1
  83. package/dist/experimental/multi-store/useStore.js +0 -16
  84. package/dist/experimental/multi-store/useStore.js.map +0 -1
  85. package/dist/experimental/multi-store/useStore.test.d.ts.map +0 -1
  86. package/dist/experimental/multi-store/useStore.test.js.map +0 -1
  87. package/src/LiveStoreContext.ts +0 -41
  88. package/src/LiveStoreProvider.test.tsx +0 -248
  89. package/src/LiveStoreProvider.tsx +0 -430
  90. package/src/experimental/multi-store/StoreRegistry.test.ts +0 -518
  91. package/src/experimental/multi-store/StoreRegistry.ts +0 -253
  92. package/src/experimental/multi-store/StoreRegistryContext.tsx +0 -23
  93. package/src/experimental/multi-store/mod.ts +0 -5
  94. package/src/experimental/multi-store/storeOptions.ts +0 -8
  95. package/src/experimental/multi-store/types.ts +0 -37
  96. package/src/experimental/multi-store/useStore.ts +0 -26
  97. /package/dist/{experimental/multi-store/useStore.test.d.ts → useStore.test.d.ts} +0 -0
package/dist/useStore.js CHANGED
@@ -1,77 +1,84 @@
1
1
  import React from 'react';
2
- import { LiveStoreContext } from "./LiveStoreContext.js";
2
+ import { useStoreRegistry } from "./StoreRegistryContext.js";
3
3
  import { useClientDocument } from "./useClientDocument.js";
4
4
  import { useQuery } from "./useQuery.js";
5
5
  /**
6
- * Augments a Store instance with React-specific methods (`useQuery`, `useClientDocument`).
7
- *
8
- * This is called automatically by `useStore()` and `LiveStoreProvider`. You typically
9
- * don't need to call it directly unless you're building custom integrations.
6
+ * Returns a store instance augmented with hooks (`store.useQuery()` and `store.useClientDocument()`) for reactive queries.
10
7
  *
11
8
  * @example
12
- * ```ts
13
- * // Usually not needed—useStore() does this automatically
14
- * const store = withReactApi(myStore)
15
- * const todos = store.useQuery(tables.todos.all())
16
- * ```
17
- */
18
- export const withReactApi = (store) => {
19
- // @ts-expect-error TODO properly implement this
20
- store.useQuery = (queryable) => useQuery(queryable, { store });
21
- // @ts-expect-error TODO properly implement this
22
- store.useClientDocument = (table, idOrOptions, options) => useClientDocument(table, idOrOptions, options, { store });
23
- return store;
24
- };
25
- /**
26
- * Returns the current Store instance from React context, augmented with React-specific methods.
27
- *
28
- * Use this hook when you need direct access to the Store for operations like
29
- * `store.commit()`, `store.subscribe()`, or accessing `store.sessionId`.
9
+ * ```tsx
10
+ * function Issue() {
11
+ * // Suspends until loaded or returns immediately if already loaded
12
+ * const issueStore = useStore(issueStoreOptions('abc123'))
13
+ * const [issue] = issueStore.useQuery(queryDb(tables.issue.select()))
30
14
  *
31
- * For reactive queries, prefer `useQuery()` or `useClientDocument()` which handle
32
- * subscriptions and re-renders automatically.
15
+ * const toggleStatus = () =>
16
+ * issueStore.commit(
17
+ * issueEvents.issueStatusChanged({
18
+ * id: issue.id,
19
+ * status: issue.status === 'done' ? 'todo' : 'done',
20
+ * }),
21
+ * )
33
22
  *
34
- * @example
35
- * ```ts
36
- * const MyComponent = () => {
37
- * const { store } = useStore()
38
- *
39
- * const handleClick = () => {
40
- * store.commit(events.todoCreated({ id: nanoid(), text: 'New todo' }))
41
- * }
23
+ * const preloadParentIssue = (issueId: string) =>
24
+ * storeRegistry.preload({
25
+ * ...issueStoreOptions(issueId),
26
+ * unusedCacheTime: 10_000,
27
+ * })
42
28
  *
43
- * return <button onClick={handleClick}>Add Todo</button>
29
+ * return (
30
+ * <>
31
+ * <h2>{issue.title}</h2>
32
+ * <button onClick={() => toggleStatus()}>Toggle Status</button>
33
+ * <button onMouseEnter={() => preloadParentIssue(issue.parentIssueId)}>Open Parent Issue</button>
34
+ * </>
35
+ * )
44
36
  * }
45
37
  * ```
46
38
  *
47
- * @example
48
- * ```ts
49
- * // Access store metadata
50
- * const { store } = useStore()
51
- * console.log('Session ID:', store.sessionId)
52
- * console.log('Client ID:', store.clientId)
53
- * ```
54
- *
55
- * @example
56
- * ```ts
57
- * // Use with an explicit store instance (bypasses context)
58
- * const { store } = useStore({ store: myExternalStore })
59
- * ```
39
+ * @remarks
40
+ * - Suspends until the store is loaded.
41
+ * - Store is cached by its `storeId` in the `StoreRegistry`. Multiple calls with the same `storeId` return the same store instance.
42
+ * - Store is cached as long as it's being used, and after `unusedCacheTime` expires (default `60_000` ms in browser, `Infinity` in non-browser)
43
+ * - Default store options can be configured in `StoreRegistry` constructor.
44
+ * - Store options are only applied when the store is loaded. Subsequent calls with different options will not affect the store if it's already loaded and cached in the registry.
60
45
  *
61
- * @throws Error if called outside of `<LiveStoreProvider>` or before the store is running
46
+ * @typeParam TSchema - The schema type for the store
47
+ * @returns The loaded store instance augmented with React hooks
48
+ * @throws unknown - store loading error or if called outside `<StoreRegistryProvider>`
62
49
  */
63
50
  export const useStore = (options) => {
64
- if (options?.store !== undefined) {
65
- return { store: withReactApi(options.store) };
66
- }
67
- // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
68
- const storeContext = React.useContext(LiveStoreContext);
69
- if (storeContext === undefined) {
70
- throw new Error(`useStore can only be used inside StoreContext.Provider`);
71
- }
72
- if (storeContext.stage !== 'running') {
73
- throw new Error(`useStore can only be used after the store is running`);
51
+ const storeRegistry = useStoreRegistry();
52
+ // NOTE: retain() is called in useEffect (after render), while getOrLoadPromise() is called
53
+ // in useMemo (during render). This creates a timing gap where with very short unusedCacheTime
54
+ // values (e.g., 0), the store could theoretically be disposed before the effect fires.
55
+ // In practice, this is not an issue with the default 60s cache time, but it becomes an issue when
56
+ // `unusedCacheTime` is configured to values less than ~100ms.
57
+ // See https://github.com/livestorejs/livestore/issues/916
58
+ React.useEffect(() => storeRegistry.retain(options), [storeRegistry, options]);
59
+ const storeOrPromise = React.useMemo(() => storeRegistry.getOrLoadPromise(options), [storeRegistry, options]);
60
+ const store = storeOrPromise instanceof Promise ? React.use(storeOrPromise) : storeOrPromise;
61
+ // Expose store on the global object for browser console debugging.
62
+ globalThis.__debugLiveStore ??= {};
63
+ if (Object.keys(globalThis.__debugLiveStore).length === 0) {
64
+ globalThis.__debugLiveStore._ = store;
74
65
  }
75
- return { store: withReactApi(storeContext.store) };
66
+ globalThis.__debugLiveStore[options.debug?.instanceId ?? options.storeId] = store;
67
+ return withReactApi(store);
68
+ };
69
+ /**
70
+ * Augments a Store instance with React-specific methods (`useQuery`, `useClientDocument`).
71
+ *
72
+ * This is called automatically by `useStore()`. You typically don't need to call it
73
+ * directly unless you're building custom integrations.
74
+ *
75
+ * @internal
76
+ */
77
+ export const withReactApi = (store) => {
78
+ // @ts-expect-error TODO properly implement this
79
+ store.useQuery = (queryable) => useQuery(queryable, { store });
80
+ // @ts-expect-error TODO properly implement this
81
+ store.useClientDocument = (table, idOrOptions, options) => useClientDocument(table, idOrOptions, options, { store });
82
+ return store;
76
83
  };
77
84
  //# sourceMappingURL=useStore.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"useStore.js","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AAEA,OAAO,KAAK,MAAM,OAAO,CAAA;AAGzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAA;AACxD,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;GAYG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAAkC,KAAqB,EAA6B,EAAE;IAChH,gDAAgD;IAEhD,KAAK,CAAC,QAAQ,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAC9D,gDAAgD;IAEhD,KAAK,CAAC,iBAAiB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACpH,OAAO,KAAkC,CAAA;AAC3C,CAAC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAqCG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAAC,OAA2B,EAA+B,EAAE;IACnF,IAAI,OAAO,EAAE,KAAK,KAAK,SAAS,EAAE,CAAC;QACjC,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,OAAO,CAAC,KAAK,CAAC,EAAE,CAAA;IAC/C,CAAC;IAED,mEAAmE;IACnE,MAAM,YAAY,GAAG,KAAK,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAA;IAEvD,IAAI,YAAY,KAAK,SAAS,EAAE,CAAC;QAC/B,MAAM,IAAI,KAAK,CAAC,wDAAwD,CAAC,CAAA;IAC3E,CAAC;IAED,IAAI,YAAY,CAAC,KAAK,KAAK,SAAS,EAAE,CAAC;QACrC,MAAM,IAAI,KAAK,CAAC,sDAAsD,CAAC,CAAA;IACzE,CAAC;IAED,OAAO,EAAE,KAAK,EAAE,YAAY,CAAC,YAAY,CAAC,KAAK,CAAC,EAAE,CAAA;AACpD,CAAC,CAAA"}
1
+ {"version":3,"file":"useStore.js","sourceRoot":"","sources":["../src/useStore.ts"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAA;AACzB,OAAO,EAAE,gBAAgB,EAAE,MAAM,2BAA4B,CAAA;AAC7D,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC1D,OAAO,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAExC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,CAAC,MAAM,QAAQ,GAAG,CAKtB,OAAoE,EAC/B,EAAE;IACvC,MAAM,aAAa,GAAG,gBAAgB,EAAE,CAAA;IAExC,2FAA2F;IAC3F,8FAA8F;IAC9F,uFAAuF;IACvF,kGAAkG;IAClG,8DAA8D;IAC9D,0DAA0D;IAC1D,KAAK,CAAC,SAAS,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,MAAM,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAA;IAE9E,MAAM,cAAc,GAAG,KAAK,CAAC,OAAO,CAAC,GAAG,EAAE,CAAC,aAAa,CAAC,gBAAgB,CAAC,OAAO,CAAC,EAAE,CAAC,aAAa,EAAE,OAAO,CAAC,CAAC,CAAA;IAE7G,MAAM,KAAK,GAAG,cAAc,YAAY,OAAO,CAAC,CAAC,CAAC,KAAK,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC,CAAC,CAAC,cAAc,CAAA;IAE5F,mEAAmE;IACnE,UAAU,CAAC,gBAAgB,KAAK,EAAE,CAAA;IAClC,IAAI,MAAM,CAAC,IAAI,CAAC,UAAU,CAAC,gBAAgB,CAAC,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAC1D,UAAU,CAAC,gBAAgB,CAAC,CAAC,GAAG,KAAK,CAAA;IACvC,CAAC;IACD,UAAU,CAAC,gBAAgB,CAAC,OAAO,CAAC,KAAK,EAAE,UAAU,IAAI,OAAO,CAAC,OAAO,CAAC,GAAG,KAAK,CAAA;IAEjF,OAAO,YAAY,CAAC,KAAK,CAAC,CAAA;AAC5B,CAAC,CAAA;AAgBD;;;;;;;GAOG;AACH,MAAM,CAAC,MAAM,YAAY,GAAG,CAC1B,KAA+B,EACM,EAAE;IACvC,gDAAgD;IAChD,KAAK,CAAC,QAAQ,GAAG,CAAC,SAAS,EAAE,EAAE,CAAC,QAAQ,CAAC,SAAS,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IAE9D,gDAAgD;IAChD,KAAK,CAAC,iBAAiB,GAAG,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,CAAC,iBAAiB,CAAC,KAAK,EAAE,WAAW,EAAE,OAAO,EAAE,EAAE,KAAK,EAAE,CAAC,CAAA;IACpH,OAAO,KAA4C,CAAA;AACrD,CAAC,CAAA"}
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStore.test.d.ts","sourceRoot":"","sources":["../src/useStore.test.tsx"],"names":[],"mappings":""}
@@ -1,22 +1,20 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
2
  import { makeInMemoryAdapter } from '@livestore/adapter-web';
3
- import { StoreInternalsSymbol } from '@livestore/livestore';
3
+ import { StoreInternalsSymbol, StoreRegistry, storeOptions, } from '@livestore/livestore';
4
4
  import { shouldNeverHappen } from '@livestore/utils';
5
5
  import { act, render, renderHook, waitFor } from '@testing-library/react';
6
6
  import * as React from 'react';
7
7
  import { describe, expect, it } from 'vitest';
8
- import { schema } from "../../__tests__/fixture.js";
9
- import { StoreRegistry } from "./StoreRegistry.js";
8
+ import { schema } from "./__tests__/fixture.js";
10
9
  import { StoreRegistryProvider } from "./StoreRegistryContext.js";
11
- import { storeOptions } from "./storeOptions.js";
12
10
  import { useStore } from "./useStore.js";
13
11
  describe('experimental useStore', () => {
14
12
  it('should return the same promise instance for concurrent getOrLoadStore calls', async () => {
15
- const registry = new StoreRegistry();
13
+ const storeRegistry = new StoreRegistry();
16
14
  const options = testStoreOptions();
17
15
  // Make two concurrent calls during loading
18
- const firstStore = registry.getOrLoadPromise(options);
19
- const secondStore = registry.getOrLoadPromise(options);
16
+ const firstStore = storeRegistry.getOrLoadPromise(options);
17
+ const secondStore = storeRegistry.getOrLoadPromise(options);
20
18
  // Both should be promises (store is loading)
21
19
  expect(firstStore).toBeInstanceOf(Promise);
22
20
  expect(secondStore).toBeInstanceOf(Promise);
@@ -28,11 +26,11 @@ describe('experimental useStore', () => {
28
26
  await cleanupAfterUnmount(() => { });
29
27
  });
30
28
  it('works with Suspense boundary', async () => {
31
- const registry = new StoreRegistry();
29
+ const storeRegistry = new StoreRegistry();
32
30
  const options = testStoreOptions();
33
31
  let view;
34
32
  await act(async () => {
35
- view = render(_jsx(StoreRegistryProvider, { storeRegistry: registry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumer, { options: options }) }) }));
33
+ view = render(_jsx(StoreRegistryProvider, { storeRegistry: storeRegistry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumer, { options: options }) }) }));
36
34
  });
37
35
  const renderedView = view ?? shouldNeverHappen('render failed');
38
36
  // After loading completes, should show the actual content
@@ -41,9 +39,9 @@ describe('experimental useStore', () => {
41
39
  await cleanupAfterUnmount(() => renderedView.unmount());
42
40
  });
43
41
  it('does not re-suspend on subsequent renders when store is already loaded', async () => {
44
- const registry = new StoreRegistry();
42
+ const storeRegistry = new StoreRegistry();
45
43
  const options = testStoreOptions();
46
- const Wrapper = ({ opts }) => (_jsx(StoreRegistryProvider, { storeRegistry: registry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumer, { options: opts }) }) }));
44
+ const Wrapper = ({ opts }) => (_jsx(StoreRegistryProvider, { storeRegistry: storeRegistry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumer, { options: opts }) }) }));
47
45
  let view;
48
46
  await act(async () => {
49
47
  view = render(_jsx(Wrapper, { opts: options }));
@@ -62,28 +60,28 @@ describe('experimental useStore', () => {
62
60
  await cleanupAfterUnmount(() => renderedView.unmount());
63
61
  });
64
62
  it('throws when store loading fails', async () => {
65
- const registry = new StoreRegistry();
63
+ const storeRegistry = new StoreRegistry();
66
64
  const badOptions = testStoreOptions({
67
65
  // @ts-expect-error - intentionally passing invalid adapter to trigger error
68
66
  adapter: null,
69
67
  });
70
68
  // Pre-load the store to cache the error (error happens synchronously)
71
- expect(() => registry.getOrLoadPromise(badOptions)).toThrow();
69
+ expect(() => storeRegistry.getOrLoadPromise(badOptions)).toThrow();
72
70
  // Now when useStore tries to get it, it should throw synchronously
73
71
  expect(() => renderHook(() => useStore(badOptions), {
74
- wrapper: makeProvider(registry),
72
+ wrapper: makeProvider(storeRegistry),
75
73
  })).toThrow();
76
74
  });
77
75
  it.each([
78
76
  { label: 'non-strict mode', strictMode: false },
79
77
  { label: 'strict mode', strictMode: true },
80
78
  ])('works in $label', async ({ strictMode }) => {
81
- const registry = new StoreRegistry();
79
+ const storeRegistry = new StoreRegistry();
82
80
  const options = testStoreOptions();
83
81
  let hook;
84
82
  await act(async () => {
85
83
  hook = renderHook(() => useStore(options), {
86
- wrapper: makeProvider(registry, { suspense: true }),
84
+ wrapper: makeProvider(storeRegistry, { suspense: true }),
87
85
  reactStrictMode: strictMode,
88
86
  });
89
87
  });
@@ -94,14 +92,14 @@ describe('experimental useStore', () => {
94
92
  await cleanupAfterUnmount(unmount);
95
93
  });
96
94
  it('handles switching between different storeId values', async () => {
97
- const registry = new StoreRegistry();
95
+ const storeRegistry = new StoreRegistry();
98
96
  const optionsA = testStoreOptions({ storeId: 'store-a' });
99
97
  const optionsB = testStoreOptions({ storeId: 'store-b' });
100
98
  let hook;
101
99
  await act(async () => {
102
100
  hook = renderHook((opts) => useStore(opts), {
103
101
  initialProps: optionsA,
104
- wrapper: makeProvider(registry, { suspense: true }),
102
+ wrapper: makeProvider(storeRegistry, { suspense: true }),
105
103
  });
106
104
  });
107
105
  const { result, rerender, unmount } = hook ?? shouldNeverHappen('renderHook failed');
@@ -126,7 +124,7 @@ describe('experimental useStore', () => {
126
124
  // useStore doesn't handle unusedCacheTime=0 correctly because retain is called in useEffect (after render)
127
125
  // See https://github.com/livestorejs/livestore/issues/916
128
126
  it.skip('should load store with unusedCacheTime set to 0', async () => {
129
- const registry = new StoreRegistry({ defaultOptions: { unusedCacheTime: 0 } });
127
+ const storeRegistry = new StoreRegistry({ defaultOptions: { unusedCacheTime: 0 } });
130
128
  const options = testStoreOptions({ unusedCacheTime: 0 });
131
129
  const StoreConsumerWithVerification = ({ opts }) => {
132
130
  const store = useStore(opts);
@@ -136,7 +134,7 @@ describe('experimental useStore', () => {
136
134
  };
137
135
  let view;
138
136
  await act(async () => {
139
- view = render(_jsx(StoreRegistryProvider, { storeRegistry: registry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumerWithVerification, { opts: options }) }) }));
137
+ view = render(_jsx(StoreRegistryProvider, { storeRegistry: storeRegistry, children: _jsx(React.Suspense, { fallback: _jsx("div", { "data-testid": "fallback" }), children: _jsx(StoreConsumerWithVerification, { opts: options }) }) }));
140
138
  });
141
139
  const renderedView = view ?? shouldNeverHappen('render failed');
142
140
  await waitForSuspenseResolved(renderedView);
@@ -154,8 +152,8 @@ const StoreConsumer = ({ options }) => {
154
152
  useStore(options);
155
153
  return _jsx("div", { "data-testid": "ready" });
156
154
  };
157
- const makeProvider = (registry, { suspense = false } = {}) => ({ children }) => {
158
- let content = _jsx(StoreRegistryProvider, { storeRegistry: registry, children: children });
155
+ const makeProvider = (storeRegistry, { suspense = false } = {}) => ({ children }) => {
156
+ let content = _jsx(StoreRegistryProvider, { storeRegistry: storeRegistry, children: children });
159
157
  if (suspense) {
160
158
  content = _jsx(React.Suspense, { fallback: null, children: content });
161
159
  }
@@ -0,0 +1 @@
1
+ {"version":3,"file":"useStore.test.js","sourceRoot":"","sources":["../src/useStore.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.4.0-dev.21",
3
+ "version": "0.4.0-dev.22",
4
4
  "type": "module",
5
5
  "sideEffects": false,
6
6
  "exports": {
@@ -9,12 +9,12 @@
9
9
  },
10
10
  "dependencies": {
11
11
  "@opentelemetry/api": "1.9.0",
12
- "@livestore/common": "0.4.0-dev.21",
13
- "@livestore/utils": "0.4.0-dev.21",
14
- "@livestore/livestore": "0.4.0-dev.21"
12
+ "@livestore/common": "0.4.0-dev.22",
13
+ "@livestore/livestore": "0.4.0-dev.22",
14
+ "@livestore/utils": "0.4.0-dev.22"
15
15
  },
16
16
  "devDependencies": {
17
- "@opentelemetry/sdk-trace-base": "2.0.1",
17
+ "@opentelemetry/sdk-trace-base": "2.2.0",
18
18
  "@testing-library/dom": "^10.4.1",
19
19
  "@testing-library/react": "^16.3.0",
20
20
  "@types/react": "19.1.13",
@@ -26,8 +26,8 @@
26
26
  "typescript": "5.9.2",
27
27
  "vite": "7.2.4",
28
28
  "vitest": "3.2.4",
29
- "@livestore/adapter-web": "0.4.0-dev.21",
30
- "@livestore/utils-dev": "0.4.0-dev.21"
29
+ "@livestore/utils-dev": "0.4.0-dev.22",
30
+ "@livestore/adapter-web": "0.4.0-dev.22"
31
31
  },
32
32
  "files": [
33
33
  "package.json",
@@ -0,0 +1,69 @@
1
+ import type { StoreRegistry } from '@livestore/livestore'
2
+ import * as React from 'react'
3
+
4
+ export const StoreRegistryContext = React.createContext<StoreRegistry | undefined>(undefined)
5
+
6
+ export type StoreRegistryProviderProps = {
7
+ storeRegistry: StoreRegistry
8
+ children: React.ReactNode
9
+ }
10
+
11
+ /**
12
+ * React context provider that makes a {@link StoreRegistry} available to descendant components.
13
+ *
14
+ * Wrap your application (or a subtree) with this provider to enable {@link useStore} and
15
+ * {@link useStoreRegistry} hooks within that tree.
16
+ *
17
+ * @example
18
+ * ```tsx
19
+ * import { StoreRegistry } from '@livestore/livestore'
20
+ * import { StoreRegistryProvider } from '@livestore/react'
21
+ * import { unstable_batchedUpdates as batchUpdates } from 'react-dom'
22
+ *
23
+ * const storeRegistry = new StoreRegistry({
24
+ * defaultOptions: { batchUpdates }
25
+ * })
26
+ *
27
+ * function App() {
28
+ * return (
29
+ * <StoreRegistryProvider storeRegistry={storeRegistry}>
30
+ * <MyComponent />
31
+ * </StoreRegistryProvider>
32
+ * )
33
+ * }
34
+ * ```
35
+ */
36
+ export const StoreRegistryProvider = ({ storeRegistry, children }: StoreRegistryProviderProps): React.JSX.Element => {
37
+ return <StoreRegistryContext value={storeRegistry}>{children}</StoreRegistryContext>
38
+ }
39
+
40
+ /**
41
+ * Hook to access the {@link StoreRegistry} from context. Useful for advanced operations like preloading.
42
+ *
43
+ * @param override - Optional registry to use instead of the context value.
44
+ * When provided, skips context lookup entirely.
45
+ * @returns The registry provided by the nearest {@link StoreRegistryProvider} ancestor, or the `override` if provided.
46
+ * @throws Error if called outside a {@link StoreRegistryProvider} and no override is provided
47
+ *
48
+ * @example
49
+ * ```tsx
50
+ * function PreloadButton({ issueId }: { issueId: string }) {
51
+ * const storeRegistry = useStoreRegistry()
52
+ *
53
+ * const handleMouseEnter = () => {
54
+ * storeRegistry.preload(issueStoreOptions(issueId))
55
+ * }
56
+ *
57
+ * return <button onMouseEnter={handleMouseEnter}>View Issue</button>
58
+ * }
59
+ * ```
60
+ */
61
+ export const useStoreRegistry = (override?: StoreRegistry) => {
62
+ if (override) return override
63
+
64
+ const storeRegistry = React.use(StoreRegistryContext)
65
+
66
+ if (!storeRegistry) throw new Error('useStoreRegistry() must be used within <StoreRegistryProvider>')
67
+
68
+ return storeRegistry
69
+ }
@@ -136,21 +136,9 @@ export const makeTodoMvcReact: (opts?: MakeTodoMvcReactOptions) => Effect.Effect
136
136
 
137
137
  const storeWithReactApi = LiveStoreReact.withReactApi(store)
138
138
 
139
- // TODO improve typing of `LiveStoreContext`
140
- const storeContext = {
141
- stage: 'running' as const,
142
- store: storeWithReactApi,
143
- }
144
-
145
139
  const MaybeStrictMode = strictMode ? React.StrictMode : React.Fragment
146
140
 
147
- const wrapper = ({ children }: any) => (
148
- <MaybeStrictMode>
149
- <LiveStoreReact.LiveStoreContext.Provider value={storeContext}>
150
- {children}
151
- </LiveStoreReact.LiveStoreContext.Provider>
152
- </MaybeStrictMode>
153
- )
141
+ const wrapper = ({ children }: any) => <MaybeStrictMode>{children}</MaybeStrictMode>
154
142
 
155
143
  const renderCount = makeRenderCount()
156
144
 
@@ -1,4 +1,4 @@
1
- import type { LiveQueryDef } from '@livestore/livestore'
1
+ import type { LiveQueryDef, Store } from '@livestore/livestore'
2
2
  import { computed } from '@livestore/livestore'
3
3
  import React from 'react'
4
4
 
@@ -16,6 +16,8 @@ export type LiveListProps<TItem> = {
16
16
  renderItem: (item: TItem, opts: { index: number; isInitialListRender: boolean }) => React.ReactNode
17
17
  /** Needs to be unique across all list items */
18
18
  getKey: (item: TItem, index: number) => string | number
19
+ /** The store instance to use for queries */
20
+ store: Store<any, any>
19
21
  }
20
22
 
21
23
  /**
@@ -26,12 +28,15 @@ export type LiveListProps<TItem> = {
26
28
  * In the future we want to make this component even more efficient by using incremental rendering (https://github.com/livestorejs/livestore/pull/55)
27
29
  * e.g. when an item is added/removed/moved to only re-render the affected DOM nodes.
28
30
  */
29
- export const LiveList = <TItem,>({ items$, renderItem, getKey }: LiveListProps<TItem>): React.ReactNode => {
31
+ export const LiveList = <TItem,>({ items$, renderItem, getKey, store }: LiveListProps<TItem>): React.ReactNode => {
30
32
  const [hasMounted, setHasMounted] = React.useState(false)
31
33
 
32
34
  React.useEffect(() => setHasMounted(true), [])
33
35
 
34
- const keys = useQuery(computed((get) => get(items$).map(getKey)))
36
+ const keys = useQuery(
37
+ computed((get) => get(items$).map(getKey)),
38
+ { store },
39
+ )
35
40
  const arr = React.useMemo(
36
41
  () =>
37
42
  keys.map(
@@ -54,6 +59,7 @@ export const LiveList = <TItem,>({ items$, renderItem, getKey }: LiveListProps<T
54
59
  key={key}
55
60
  itemKey={key}
56
61
  item$={item$}
62
+ store={store}
57
63
  opts={{ isInitialListRender: !hasMounted, index }}
58
64
  renderItem={renderItem}
59
65
  />
@@ -66,13 +72,15 @@ const ItemWrapper = <TItem,>({
66
72
  item$,
67
73
  opts,
68
74
  renderItem,
75
+ store,
69
76
  }: {
70
77
  itemKey: string | number
71
78
  item$: LiveQueryDef<TItem>
72
79
  opts: { index: number; isInitialListRender: boolean }
73
80
  renderItem: (item: TItem, opts: { index: number; isInitialListRender: boolean }) => React.ReactNode
81
+ store: Store<any, any>
74
82
  }) => {
75
- const item = useQuery(item$)
83
+ const item = useQuery(item$, { store })
76
84
 
77
85
  return <>{renderItem(item, opts)}</>
78
86
  }
@@ -82,6 +90,7 @@ const ItemWrapperMemo = React.memo(
82
90
  (prev, next) =>
83
91
  prev.itemKey === next.itemKey &&
84
92
  prev.renderItem === next.renderItem &&
93
+ prev.store === next.store &&
85
94
  prev.opts.index === next.opts.index &&
86
95
  prev.opts.isInitialListRender === next.opts.isInitialListRender,
87
96
  ) as typeof ItemWrapper
@@ -1,2 +1 @@
1
1
  export { LiveList, type LiveListProps } from './components/LiveList.tsx'
2
- export * from './multi-store/mod.ts'
package/src/mod.ts CHANGED
@@ -1,5 +1,6 @@
1
- export { LiveStoreContext, type ReactApi } from './LiveStoreContext.ts'
2
- export { LiveStoreProvider } from './LiveStoreProvider.tsx'
1
+ export { StoreRegistry, storeOptions } from '@livestore/livestore'
2
+ export { LiveList, type LiveListProps } from './experimental/components/LiveList.tsx'
3
+ export * from './StoreRegistryContext.tsx'
3
4
  export {
4
5
  type Dispatch,
5
6
  type SetStateAction,
@@ -9,5 +10,5 @@ export {
9
10
  useClientDocument,
10
11
  } from './useClientDocument.ts'
11
12
  export { useQuery, useQueryRef } from './useQuery.ts'
12
- export { useStore, withReactApi } from './useStore.ts'
13
+ export { type ReactApi, useStore, withReactApi } from './useStore.ts'
13
14
  export { useStackInfo } from './utils/stack-info.ts'
@@ -6,7 +6,6 @@ import { queryDb } from '@livestore/livestore'
6
6
  import { omitUndefineds, shouldNeverHappen } from '@livestore/utils'
7
7
  import React from 'react'
8
8
 
9
- import { LiveStoreContext } from './LiveStoreContext.ts'
10
9
  import { useQueryRef } from './useQuery.ts'
11
10
 
12
11
  /**
@@ -115,10 +114,7 @@ export const useClientDocument: {
115
114
 
116
115
  const tableName = table.sqliteDef.name
117
116
 
118
- const store =
119
- storeArg?.store ?? // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
120
- React.useContext(LiveStoreContext)?.store ??
121
- shouldNeverHappen(`No store provided to useClientDocument`)
117
+ const store = storeArg?.store ?? shouldNeverHappen(`No store provided to useClientDocument`)
122
118
 
123
119
  // console.debug('useClientDocument', tableName, id)
124
120
 
package/src/useQuery.ts CHANGED
@@ -14,7 +14,6 @@ import { deepEqual, indent, shouldNeverHappen } from '@livestore/utils'
14
14
  import * as otel from '@opentelemetry/api'
15
15
  import React from 'react'
16
16
 
17
- import { LiveStoreContext } from './LiveStoreContext.ts'
18
17
  import { useRcResource } from './useRcResource.ts'
19
18
  import { originalStackLimit } from './utils/stack-info.ts'
20
19
  import { useStateRefWithReactiveInput } from './utils/useStateRefWithReactiveInput.ts'
@@ -49,7 +48,7 @@ export const useQuery = <TQueryable extends Queryable<any>>(
49
48
  *
50
49
  * Parameters
51
50
  * - `queryable`: The query definition/instance/builder to run and subscribe to.
52
- * - `options.store`: Optional store to use; by default the store from `LiveStoreContext` is used.
51
+ * - `options.store`: The store to use. Required when calling `useQueryRef` directly; automatically provided when using `store.useQuery()`.
53
52
  * - `options.otelContext`: Optional parent otel context for the query span.
54
53
  * - `options.otelSpanName`: Optional explicit span name; otherwise derived from the query label.
55
54
  *
@@ -71,10 +70,7 @@ export const useQueryRef = <TQueryable extends Queryable<any>>(
71
70
  valueRef: React.RefObject<Queryable.Result<TQueryable>>
72
71
  queryRcRef: LiveQueries.RcRef<LiveQuery<Queryable.Result<TQueryable>>>
73
72
  } => {
74
- const store =
75
- options?.store ?? // biome-ignore lint/correctness/useHookAtTopLevel: store is stable
76
- React.useContext(LiveStoreContext)?.store ??
77
- shouldNeverHappen(`No store provided to useQuery`)
73
+ const store = options?.store ?? shouldNeverHappen(`No store provided to useQuery`)
78
74
 
79
75
  type TResult = Queryable.Result<TQueryable>
80
76
  type NormalizedQueryable =