@livestore/react 0.3.0-dev.15 → 0.3.0-dev.16

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.
@@ -1,128 +0,0 @@
1
- import * as ReactTesting from '@testing-library/react';
2
- import * as React from 'react';
3
- import { beforeEach, describe, expect, it, vi } from 'vitest';
4
- import { __resetUseRcRefCache, useRcRef } from './useRcResource.js';
5
- describe.each([{ strictMode: true }, { strictMode: false }])('useRcRef (strictMode=%s)', ({ strictMode }) => {
6
- beforeEach(() => {
7
- __resetUseRcRefCache();
8
- });
9
- const wrapper = strictMode ? React.StrictMode : React.Fragment;
10
- it('should create a stateful entity using make and call cleanup on unmount', () => {
11
- const makeSpy = vi.fn(() => Symbol('statefulResource'));
12
- const cleanupSpy = vi.fn();
13
- const { result, unmount } = ReactTesting.renderHook(() => useRcRef('key-1', makeSpy, cleanupSpy), { wrapper });
14
- expect(makeSpy).toHaveBeenCalledTimes(1);
15
- expect(result.current).toBeDefined();
16
- expect(cleanupSpy).toHaveBeenCalledTimes(0);
17
- unmount();
18
- expect(cleanupSpy).toHaveBeenCalledTimes(1);
19
- });
20
- it('should reuse the same entity when the key remains unchanged', () => {
21
- const makeSpy = vi.fn(() => Symbol('statefulResource'));
22
- const cleanupSpy = vi.fn();
23
- const { result, rerender, unmount } = ReactTesting.renderHook(({ key }) => useRcRef(key, makeSpy, cleanupSpy), {
24
- initialProps: { key: 'consistent-key' },
25
- wrapper,
26
- });
27
- const instance1 = result.current;
28
- // Re-render with the same key
29
- rerender({ key: 'consistent-key' });
30
- const instance2 = result.current;
31
- expect(instance1).toBe(instance2);
32
- expect(makeSpy).toHaveBeenCalledTimes(1);
33
- unmount();
34
- expect(cleanupSpy).toHaveBeenCalledTimes(1);
35
- });
36
- it('should dispose the previous instance when the key changes', () => {
37
- const makeSpy = vi.fn(() => Symbol('statefulResource'));
38
- const cleanupSpy = vi.fn();
39
- const { result, rerender, unmount } = ReactTesting.renderHook(({ key }) => useRcRef(key, makeSpy, cleanupSpy), {
40
- initialProps: { key: 'a' },
41
- wrapper,
42
- });
43
- const instanceA = result.current;
44
- // Change the key; this should trigger the disposal of the 'a' instance
45
- rerender({ key: 'b' });
46
- const instanceB = result.current;
47
- expect(instanceA).not.toBe(instanceB);
48
- expect(makeSpy).toHaveBeenCalledTimes(2);
49
- expect(cleanupSpy).toHaveBeenCalledTimes(1);
50
- unmount();
51
- expect(cleanupSpy).toHaveBeenCalledTimes(2);
52
- });
53
- it('should not dispose the entity until all consumers unmount', () => {
54
- const makeSpy = vi.fn(() => Symbol('statefulResource'));
55
- const cleanupSpy = vi.fn();
56
- // Simulate two consumers using the same key independently.
57
- const { unmount: unmount1 } = ReactTesting.renderHook(() => useRcRef('shared-key', makeSpy, cleanupSpy), {
58
- wrapper,
59
- });
60
- const { unmount: unmount2, result } = ReactTesting.renderHook(() => useRcRef('shared-key', makeSpy, cleanupSpy), {
61
- wrapper,
62
- });
63
- expect(result.current).toBeDefined();
64
- expect(makeSpy).toHaveBeenCalledTimes(1);
65
- // Unmount first consumer; the entity should remain active.
66
- unmount1();
67
- expect(cleanupSpy).not.toHaveBeenCalled();
68
- // Unmount second consumer; now the entity is disposed.
69
- unmount2();
70
- expect(cleanupSpy).toHaveBeenCalledTimes(1);
71
- });
72
- it('should handle rapid key changes correctly', () => {
73
- const makeSpy = vi.fn(() => Symbol('statefulResource'));
74
- const cleanupSpy = vi.fn();
75
- const { rerender, unmount } = ReactTesting.renderHook(({ key }) => useRcRef(key, makeSpy, cleanupSpy), {
76
- initialProps: { key: '1' },
77
- wrapper,
78
- });
79
- // Rapid sequence of key changes.
80
- rerender({ key: '2' });
81
- rerender({ key: '3' });
82
- // Expect three creations: one each for keys '1', '2', '3'
83
- expect(makeSpy).toHaveBeenCalledTimes(3);
84
- // Cleanup should have been triggered for key '1' and key '2'
85
- expect(cleanupSpy).toHaveBeenCalledTimes(2);
86
- unmount();
87
- // Unmounting the final consumer disposes the key '3' instance.
88
- expect(cleanupSpy).toHaveBeenCalledTimes(3);
89
- });
90
- });
91
- // This code was useful to better understand the hook behaviour with and without strict mode
92
- // describe('debug', () => {
93
- // const useStrictTest = (key: string) => {
94
- // const id = React.useId()
95
- // console.log(key, 'id', id)
96
- // const x = React.useMemo(() => {
97
- // console.log('useMemo', key)
98
- // return 'hi' + key
99
- // }, [key])
100
- // React.useEffect(() => {
101
- // console.log('useEffect', key)
102
- // return () => {
103
- // console.log('unmount', key)
104
- // }
105
- // }, [])
106
- // return x
107
- // }
108
- // it('strict mode component', () => {
109
- // console.log('strict mode component')
110
- // const Root = () => {
111
- // useStrictTest('a')
112
- // return null
113
- // }
114
- // const { unmount } = ReactTesting.render(
115
- // <React.StrictMode>
116
- // <Root />
117
- // </React.StrictMode>,
118
- // )
119
- // unmount()
120
- // })
121
- // it('strict mode hook', () => {
122
- // console.log('strict mode hook')
123
- // const wrapper: React.FC<{ children: React.ReactNode }> = React.StrictMode
124
- // const { unmount } = ReactTesting.renderHook(() => useStrictTest('b'), { wrapper })
125
- // unmount()
126
- // })
127
- // })
128
- //# sourceMappingURL=useRcRef.test.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useRcRef.test.js","sourceRoot":"","sources":["../src/useRcRef.test.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,YAAY,MAAM,wBAAwB,CAAA;AACtD,OAAO,KAAK,KAAK,MAAM,OAAO,CAAA;AAC9B,OAAO,EAAE,UAAU,EAAE,QAAQ,EAAE,MAAM,EAAE,EAAE,EAAE,EAAE,EAAE,MAAM,QAAQ,CAAA;AAE7D,OAAO,EAAE,oBAAoB,EAAE,QAAQ,EAAE,MAAM,oBAAoB,CAAA;AAEnE,QAAQ,CAAC,IAAI,CAAC,CAAC,EAAE,UAAU,EAAE,IAAI,EAAE,EAAE,EAAE,UAAU,EAAE,KAAK,EAAE,CAAC,CAAC,CAAC,0BAA0B,EAAE,CAAC,EAAE,UAAU,EAAE,EAAE,EAAE;IAC1G,UAAU,CAAC,GAAG,EAAE;QACd,oBAAoB,EAAE,CAAA;IACxB,CAAC,CAAC,CAAA;IAEF,MAAM,OAAO,GAAG,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,KAAK,CAAC,QAAQ,CAAA;IAE9D,EAAE,CAAC,wEAAwE,EAAE,GAAG,EAAE;QAChF,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACvD,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE1B,MAAM,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,OAAO,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAE9G,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACxC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;QAEpC,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAC3C,OAAO,EAAE,CAAA;QACT,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,6DAA6D,EAAE,GAAG,EAAE;QACrE,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACvD,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE1B,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YAC7G,YAAY,EAAE,EAAE,GAAG,EAAE,gBAAgB,EAAE;YACvC,OAAO;SACR,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAA;QAEhC,8BAA8B;QAC9B,QAAQ,CAAC,EAAE,GAAG,EAAE,gBAAgB,EAAE,CAAC,CAAA;QACnC,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAA;QAEhC,MAAM,CAAC,SAAS,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACjC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAExC,OAAO,EAAE,CAAA;QACT,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACvD,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE1B,MAAM,EAAE,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YAC7G,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;YAC1B,OAAO;SACR,CAAC,CAAA;QAEF,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAA;QAEhC,uEAAuE;QACvE,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;QACtB,MAAM,SAAS,GAAG,MAAM,CAAC,OAAO,CAAA;QAEhC,MAAM,CAAC,SAAS,CAAC,CAAC,GAAG,CAAC,IAAI,CAAC,SAAS,CAAC,CAAA;QACrC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACxC,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAE3C,OAAO,EAAE,CAAA;QACT,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2DAA2D,EAAE,GAAG,EAAE;QACnE,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACvD,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE1B,2DAA2D;QAC3D,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YACvG,OAAO;SACR,CAAC,CAAA;QACF,MAAM,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,GAAG,EAAE,CAAC,QAAQ,CAAC,YAAY,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YAC/G,OAAO;SACR,CAAC,CAAA;QAEF,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,WAAW,EAAE,CAAA;QACpC,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAExC,2DAA2D;QAC3D,QAAQ,EAAE,CAAA;QACV,MAAM,CAAC,UAAU,CAAC,CAAC,GAAG,CAAC,gBAAgB,EAAE,CAAA;QAEzC,uDAAuD;QACvD,QAAQ,EAAE,CAAA;QACV,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;IAEF,EAAE,CAAC,2CAA2C,EAAE,GAAG,EAAE;QACnD,MAAM,OAAO,GAAG,EAAE,CAAC,EAAE,CAAC,GAAG,EAAE,CAAC,MAAM,CAAC,kBAAkB,CAAC,CAAC,CAAA;QACvD,MAAM,UAAU,GAAG,EAAE,CAAC,EAAE,EAAE,CAAA;QAE1B,MAAM,EAAE,QAAQ,EAAE,OAAO,EAAE,GAAG,YAAY,CAAC,UAAU,CAAC,CAAC,EAAE,GAAG,EAAE,EAAE,EAAE,CAAC,QAAQ,CAAC,GAAG,EAAE,OAAO,EAAE,UAAU,CAAC,EAAE;YACrG,YAAY,EAAE,EAAE,GAAG,EAAE,GAAG,EAAE;YAC1B,OAAO;SACR,CAAC,CAAA;QAEF,iCAAiC;QACjC,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;QACtB,QAAQ,CAAC,EAAE,GAAG,EAAE,GAAG,EAAE,CAAC,CAAA;QAEtB,0DAA0D;QAC1D,MAAM,CAAC,OAAO,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QACxC,6DAA6D;QAC7D,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;QAE3C,OAAO,EAAE,CAAA;QACT,+DAA+D;QAC/D,MAAM,CAAC,UAAU,CAAC,CAAC,qBAAqB,CAAC,CAAC,CAAC,CAAA;IAC7C,CAAC,CAAC,CAAA;AACJ,CAAC,CAAC,CAAA;AAEF,4FAA4F;AAC5F,4BAA4B;AAC5B,4CAA4C;AAC5C,6BAA6B;AAC7B,+BAA+B;AAE/B,oCAAoC;AACpC,kCAAkC;AAClC,wBAAwB;AACxB,cAAc;AAEd,4BAA4B;AAC5B,oCAAoC;AACpC,qBAAqB;AACrB,oCAAoC;AACpC,QAAQ;AACR,WAAW;AAEX,aAAa;AACb,IAAI;AAEJ,wCAAwC;AACxC,2CAA2C;AAC3C,2BAA2B;AAC3B,2BAA2B;AAC3B,oBAAoB;AACpB,QAAQ;AACR,+CAA+C;AAC/C,2BAA2B;AAC3B,mBAAmB;AACnB,6BAA6B;AAC7B,QAAQ;AAER,gBAAgB;AAChB,OAAO;AAEP,mCAAmC;AACnC,sCAAsC;AACtC,gFAAgF;AAChF,yFAAyF;AAEzF,gBAAgB;AAChB,OAAO;AACP,KAAK"}
@@ -1,39 +0,0 @@
1
- import type { QueryInfo } from '@livestore/common';
2
- import type { LiveQuery, LiveQueryDef, Store } from '@livestore/livestore';
3
- import * as otel from '@opentelemetry/api';
4
- import React from 'react';
5
- export type DepKey = string | number | ReadonlyArray<string | number>;
6
- /**
7
- * Creates a query, subscribes and destroys it when the component unmounts.
8
- *
9
- * The `key` is used to determine whether the a new query should be created or if the existing one should be reused.
10
- * This hook should be used instead of `useQuery` when the query should be dynamically created based on some props.
11
- * Otherwise when using `useQuery` the query will be leaked (i.e. never destroyed) when the component re-renders/unmounts.
12
- *
13
- * Example:
14
- * ```tsx
15
- * const issue = useScopedQuery(() => queryDb(tables.issues.query.where('id', issueId).first()), ['issue-details', issueId])
16
- * ```
17
- *
18
- * Important: On Expo/React Native please make sure the key contains a globally unique identifier, otherwise the query might get reused unintentionally.
19
- * Example: `['issue-details', issueId]`
20
- * See this issue to track progress: https://github.com/livestorejs/livestore/issues/231
21
- */
22
- export declare const useScopedQuery: <TResult>(makeQuery: () => LiveQueryDef<TResult>, key: DepKey, options?: {
23
- store?: Store;
24
- }) => TResult;
25
- export declare const useScopedQueryRef: <TResult>(makeQuery: () => LiveQueryDef<TResult>, key: DepKey, options?: {
26
- store?: Store;
27
- }) => React.RefObject<TResult>;
28
- export declare const useMakeScopedQuery: <TResult, TQueryInfo extends QueryInfo>(makeQueryDef: (otelContext: otel.Context) => LiveQueryDef<TResult, TQueryInfo>, key: DepKey, options?: {
29
- store?: Store;
30
- otel?: {
31
- spanName?: string;
32
- attributes?: otel.Attributes;
33
- };
34
- }) => {
35
- query$: LiveQuery<TResult, TQueryInfo>;
36
- queryDef: LiveQueryDef<TResult, TQueryInfo>;
37
- otelContext: otel.Context;
38
- };
39
- //# sourceMappingURL=useScopedQuery.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useScopedQuery.d.ts","sourceRoot":"","sources":["../src/useScopedQuery.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,mBAAmB,CAAA;AAClD,OAAO,KAAK,EAAE,SAAS,EAAE,YAAY,EAAE,KAAK,EAAE,MAAM,sBAAsB,CAAA;AAC1E,OAAO,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAC1C,OAAO,KAAK,MAAM,OAAO,CAAA;AAyBzB,MAAM,MAAM,MAAM,GAAG,MAAM,GAAG,MAAM,GAAG,aAAa,CAAC,MAAM,GAAG,MAAM,CAAC,CAAA;AAErE;;;;;;;;;;;;;;;GAeG;AACH,eAAO,MAAM,cAAc,GAAI,OAAO,aACzB,MAAM,YAAY,CAAC,OAAO,CAAC,OACjC,MAAM,YACD;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,KAC1B,OAA6D,CAAA;AAEhE,eAAO,MAAM,iBAAiB,GAAI,OAAO,aAC5B,MAAM,YAAY,CAAC,OAAO,CAAC,OACjC,MAAM,YACD;IAAE,KAAK,CAAC,EAAE,KAAK,CAAA;CAAE,KAC1B,KAAK,CAAC,SAAS,CAAC,OAAO,CAIzB,CAAA;AAED,eAAO,MAAM,kBAAkB,GAAI,OAAO,EAAE,UAAU,SAAS,SAAS,gBACxD,CAAC,WAAW,EAAE,IAAI,CAAC,OAAO,KAAK,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,OACzE,MAAM,YACD;IACR,KAAK,CAAC,EAAE,KAAK,CAAA;IACb,IAAI,CAAC,EAAE;QACL,QAAQ,CAAC,EAAE,MAAM,CAAA;QACjB,UAAU,CAAC,EAAE,IAAI,CAAC,UAAU,CAAA;KAC7B,CAAA;CACF,KACA;IACD,MAAM,EAAE,SAAS,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IACtC,QAAQ,EAAE,YAAY,CAAC,OAAO,EAAE,UAAU,CAAC,CAAA;IAC3C,WAAW,EAAE,IAAI,CAAC,OAAO,CAAA;CAqC1B,CAAA"}
@@ -1,131 +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
- import { useRcRef } from './useRcResource.js';
6
- // NOTE Given `useMemo` will be called multiple times (e.g. when using React Strict mode or Fast Refresh),
7
- // we are using this cache to avoid starting multiple queries/spans for the same component.
8
- // This is somewhat against some recommended React best practices, but it should be fine in our case below.
9
- // Please definitely open an issue if you see or run into any problems with this approach!
10
- const cache = new Map();
11
- /**
12
- * Creates a query, subscribes and destroys it when the component unmounts.
13
- *
14
- * The `key` is used to determine whether the a new query should be created or if the existing one should be reused.
15
- * This hook should be used instead of `useQuery` when the query should be dynamically created based on some props.
16
- * Otherwise when using `useQuery` the query will be leaked (i.e. never destroyed) when the component re-renders/unmounts.
17
- *
18
- * Example:
19
- * ```tsx
20
- * const issue = useScopedQuery(() => queryDb(tables.issues.query.where('id', issueId).first()), ['issue-details', issueId])
21
- * ```
22
- *
23
- * Important: On Expo/React Native please make sure the key contains a globally unique identifier, otherwise the query might get reused unintentionally.
24
- * Example: `['issue-details', issueId]`
25
- * See this issue to track progress: https://github.com/livestorejs/livestore/issues/231
26
- */
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];
31
- };
32
- export const useMakeScopedQuery = (makeQueryDef, key, options) => {
33
- const { store } = useStore({ store: options?.store });
34
- const fullKey = React.useMemo(
35
- // NOTE We're using the `makeQuery` function body string to make sure the key is unique across the app
36
- // TODO we should figure out whether this could cause some problems and/or if there's a better way to do this
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, () => {
40
- const spanName = options?.otel?.spanName ?? `LiveStore:useScopedQuery:${key}`;
41
- const span = store.otel.tracer.startSpan(spanName, { attributes: options?.otel?.attributes }, store.otel.queriesSpanContext);
42
- const otelContext = otel.trace.setSpan(otel.context.active(), span);
43
- // console.debug('useScopedQuery:startSpan', fullKey, spanName)
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 };
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
- // }
131
- //# sourceMappingURL=useScopedQuery.js.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useScopedQuery.js","sourceRoot":"","sources":["../src/useScopedQuery.ts"],"names":[],"mappings":"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"}
@@ -1,2 +0,0 @@
1
- export {};
2
- //# sourceMappingURL=useScopedQuery.test.d.ts.map
@@ -1 +0,0 @@
1
- {"version":3,"file":"useScopedQuery.test.d.ts","sourceRoot":"","sources":["../src/useScopedQuery.test.tsx"],"names":[],"mappings":""}
@@ -1,61 +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 { Vitest } from '@livestore/utils/node-vitest';
5
- import { render, renderHook } from '@testing-library/react';
6
- import React from 'react';
7
- // @ts-expect-error no types
8
- import * as ReactWindow from 'react-window';
9
- import { expect } from 'vitest';
10
- import { makeTodoMvcReact, tables, todos } from './__tests__/fixture.js';
11
- import * as LiveStoreReact from './mod.js';
12
- Vitest.describe('useScopedQuery', () => {
13
- Vitest.scopedLive('simple', () => Effect.gen(function* () {
14
- const { wrapper, store, renderCount } = yield* makeTodoMvcReact();
15
- store.mutate(todos.insert({ id: 't1', text: 'buy milk', completed: false }), todos.insert({ id: 't2', text: 'buy bread', completed: false }));
16
- const queryMap = new Map();
17
- const getRuns = (key) => store.reactivityGraph.context.liveQueryRCMap.get(queryMap.get(key).hash).query$.runs;
18
- const { rerender, result, unmount } = renderHook((id) => {
19
- renderCount.inc();
20
- return LiveStoreReact.useScopedQuery(() => {
21
- const query$ = queryDb({
22
- query: `select * from todos where id = '${id}'`,
23
- schema: Schema.Array(tables.todos.schema),
24
- });
25
- queryMap.set(id, query$);
26
- return query$;
27
- }, id);
28
- }, { wrapper, initialProps: 't1' });
29
- expect(result.current.length).toBe(1);
30
- expect(result.current[0].text).toBe('buy milk');
31
- expect(renderCount.val).toBe(1);
32
- expect(getRuns('t1')).toBe(1);
33
- rerender('t2');
34
- expect(result.current.length).toBe(1);
35
- expect(result.current[0].text).toBe('buy bread');
36
- expect(renderCount.val).toBe(2);
37
- expect(getRuns('t1')).toBe(1);
38
- expect(getRuns('t2')).toBe(1);
39
- unmount();
40
- expect(getRuns('t2')).toBe(1);
41
- }));
42
- // NOTE this test covers some special react lifecyle paths which I couldn't easily reproduce without react-window
43
- // it basically causes a "query swap" in the `useMemo` and both a `useEffect` cleanup call.
44
- // To handle this properly we introduced the `_tag: 'destroyed'` state in the `spanAlreadyStartedCache`.
45
- Vitest.scopedLive('should work for a list with react-window', () => Effect.gen(function* () {
46
- const { wrapper } = yield* makeTodoMvcReact();
47
- const ListWrapper = ({ numItems }) => {
48
- return (React.createElement(ReactWindow.FixedSizeList, { height: 100, width: 100, itemSize: 10, itemCount: numItems, itemData: Array.from({ length: numItems }, (_, i) => i).reverse() }, ListItem));
49
- };
50
- const ListItem = ({ data: ids, index }) => {
51
- const id = ids[index];
52
- const res = LiveStoreReact.useScopedQuery(() => LiveStore.computed(() => id, { label: `ListItem.${id}` }), id);
53
- return React.createElement("div", { role: "listitem" }, res);
54
- };
55
- const renderResult = render(React.createElement(ListWrapper, { numItems: 1 }), { wrapper });
56
- expect(renderResult.container.textContent).toBe('0');
57
- renderResult.rerender(React.createElement(ListWrapper, { numItems: 2 }));
58
- expect(renderResult.container.textContent).toBe('10');
59
- }));
60
- });
61
- //# sourceMappingURL=useScopedQuery.test.js.map
@@ -1 +0,0 @@
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,MAAM,8BAA8B,CAAA;AACrD,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,EAAY,MAAM,EAAM,MAAM,QAAQ,CAAA;AAE7C,OAAO,EAAE,gBAAgB,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,wBAAwB,CAAA;AACxE,OAAO,KAAK,cAAc,MAAM,UAAU,CAAA;AAE1C,MAAM,CAAC,QAAQ,CAAC,gBAAgB,EAAE,GAAG,EAAE;IACrC,MAAM,CAAC,UAAU,CAAC,QAAQ,EAAE,GAAG,EAAE,CAC/B,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,EAAE,OAAO,EAAE,KAAK,EAAE,WAAW,EAAE,GAAG,KAAK,CAAC,CAAC,gBAAgB,EAAE,CAAA;QAEjE,KAAK,CAAC,MAAM,CACV,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,UAAU,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,EAC9D,KAAK,CAAC,MAAM,CAAC,EAAE,EAAE,EAAE,IAAI,EAAE,IAAI,EAAE,WAAW,EAAE,SAAS,EAAE,KAAK,EAAE,CAAC,CAChE,CAAA;QAED,MAAM,QAAQ,GAAG,IAAI,GAAG,EAAuC,CAAA;QAE/D,MAAM,OAAO,GAAG,CAAC,GAAW,EAAE,EAAE,CAC9B,KAAK,CAAC,eAAe,CAAC,OAAQ,CAAC,cAAc,CAAC,GAAG,CAAC,QAAQ,CAAC,GAAG,CAAC,GAAG,CAAE,CAAC,IAAI,CAAE,CAAC,MAAM,CAAC,IAAI,CAAA;QAEzF,MAAM,EAAE,QAAQ,EAAE,MAAM,EAAE,OAAO,EAAE,GAAG,UAAU,CAC9C,CAAC,EAAU,EAAE,EAAE;YACb,WAAW,CAAC,GAAG,EAAE,CAAA;YAEjB,OAAO,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE;gBACxC,MAAM,MAAM,GAAG,OAAO,CAAC;oBACrB,KAAK,EAAE,mCAAmC,EAAE,GAAG;oBAC/C,MAAM,EAAE,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC,KAAK,CAAC,MAAM,CAAC;iBAC1C,CAAC,CAAA;gBACF,QAAQ,CAAC,GAAG,CAAC,EAAE,EAAE,MAAM,CAAC,CAAA;gBACxB,OAAO,MAAM,CAAA;YACf,CAAC,EAAE,EAAE,CAAC,CAAA;QACR,CAAC,EACD,EAAE,OAAO,EAAE,YAAY,EAAE,IAAI,EAAE,CAChC,CAAA;QAED,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,UAAU,CAAC,CAAA;QAChD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAE7B,QAAQ,CAAC,IAAI,CAAC,CAAA;QAEd,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,MAAM,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QACrC,MAAM,CAAC,MAAM,CAAC,OAAO,CAAC,CAAC,CAAE,CAAC,IAAI,CAAC,CAAC,IAAI,CAAC,WAAW,CAAC,CAAA;QACjD,MAAM,CAAC,WAAW,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC/B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAC7B,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;QAE7B,OAAO,EAAE,CAAA;QAET,MAAM,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,IAAI,CAAC,CAAC,CAAC,CAAA;IAC/B,CAAC,CAAC,CACH,CAAA;IAED,iHAAiH;IACjH,2FAA2F;IAC3F,wGAAwG;IACxG,MAAM,CAAC,UAAU,CAAC,0CAA0C,EAAE,GAAG,EAAE,CACjE,MAAM,CAAC,GAAG,CAAC,QAAQ,CAAC;QAClB,MAAM,EAAE,OAAO,EAAE,GAAG,KAAK,CAAC,CAAC,gBAAgB,EAAE,CAAA;QAE7C,MAAM,WAAW,GAAmC,CAAC,EAAE,QAAQ,EAAE,EAAE,EAAE;YACnE,OAAO,CACL,oBAAC,WAAW,CAAC,aAAa,IACxB,MAAM,EAAE,GAAG,EACX,KAAK,EAAE,GAAG,EACV,QAAQ,EAAE,EAAE,EACZ,SAAS,EAAE,QAAQ,EACnB,QAAQ,EAAE,KAAK,CAAC,IAAI,CAAC,EAAE,MAAM,EAAE,QAAQ,EAAE,EAAE,CAAC,CAAC,EAAE,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,CAAC,OAAO,EAAE,IAEhE,QAAQ,CACiB,CAC7B,CAAA;QACH,CAAC,CAAA;QAED,MAAM,QAAQ,GAA6D,CAAC,EAAE,IAAI,EAAE,GAAG,EAAE,KAAK,EAAE,EAAE,EAAE;YAClG,MAAM,EAAE,GAAG,GAAG,CAAC,KAAK,CAAE,CAAA;YACtB,MAAM,GAAG,GAAG,cAAc,CAAC,cAAc,CAAC,GAAG,EAAE,CAAC,SAAS,CAAC,QAAQ,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,EAAE,KAAK,EAAE,YAAY,EAAE,EAAE,EAAE,CAAC,EAAE,EAAE,CAAC,CAAA;YAC9G,OAAO,6BAAK,IAAI,EAAC,UAAU,IAAE,GAAG,CAAO,CAAA;QACzC,CAAC,CAAA;QAED,MAAM,YAAY,GAAG,MAAM,CAAC,oBAAC,WAAW,IAAC,QAAQ,EAAE,CAAC,GAAI,EAAE,EAAE,OAAO,EAAE,CAAC,CAAA;QAEtE,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAA;QAEpD,YAAY,CAAC,QAAQ,CAAC,oBAAC,WAAW,IAAC,QAAQ,EAAE,CAAC,GAAI,CAAC,CAAA;QAEnD,MAAM,CAAC,YAAY,CAAC,SAAS,CAAC,WAAW,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;IACvD,CAAC,CAAC,CACH,CAAA;AACH,CAAC,CAAC,CAAA"}