@livestore/react 0.3.0-dev.5 → 0.3.0-dev.50
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/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 +31 -13
- package/dist/LiveStoreProvider.d.ts.map +1 -1
- package/dist/LiveStoreProvider.js +85 -52
- package/dist/LiveStoreProvider.js.map +1 -1
- package/dist/LiveStoreProvider.test.js +98 -26
- package/dist/LiveStoreProvider.test.js.map +1 -1
- package/dist/__tests__/fixture.d.ts +121 -555
- package/dist/__tests__/fixture.d.ts.map +1 -1
- package/dist/__tests__/fixture.js +69 -28
- 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 +7 -4
- 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 +180 -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 +66 -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 +76 -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 +150 -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 +1 -1
- package/dist/utils/useStateRefWithReactiveInput.d.ts.map +1 -1
- package/dist/utils/useStateRefWithReactiveInput.js.map +1 -1
- package/package.json +26 -19
- package/src/LiveStoreContext.ts +11 -16
- package/src/LiveStoreProvider.test.tsx +176 -37
- package/src/LiveStoreProvider.tsx +159 -82
- package/src/__snapshots__/useClientDocument.test.tsx.snap +613 -0
- package/src/__snapshots__/useQuery.test.tsx.snap +2011 -0
- package/src/__tests__/fixture.tsx +75 -48
- 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 +171 -76
- package/src/useQuery.ts +91 -58
- package/src/useRcResource.test.tsx +167 -0
- package/src/useRcResource.ts +180 -0
- package/src/useStore.ts +36 -0
- package/src/utils/useStateRefWithReactiveInput.ts +1 -1
- 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 -206
- 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 -86
- 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 -367
- package/src/useAtom.ts +0 -52
- package/src/useRow.test.tsx +0 -343
- package/src/useRow.ts +0 -188
- package/src/useScopedQuery.test.tsx +0 -96
- package/src/useScopedQuery.ts +0 -142
- package/tsconfig.json +0 -20
- package/vitest.config.js +0 -17
|
@@ -1,7 +1,13 @@
|
|
|
1
|
-
import type {
|
|
1
|
+
import type { LiveStoreContextRunning } from '@livestore/livestore';
|
|
2
2
|
import React from 'react';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import type { useClientDocument } from './useClientDocument.js';
|
|
4
|
+
import type { useQuery } from './useQuery.js';
|
|
5
|
+
export type ReactApi = {
|
|
6
|
+
useQuery: typeof useQuery;
|
|
7
|
+
useClientDocument: typeof useClientDocument;
|
|
6
8
|
};
|
|
9
|
+
export declare const LiveStoreContext: React.Context<{
|
|
10
|
+
stage: "running";
|
|
11
|
+
store: LiveStoreContextRunning["store"] & ReactApi;
|
|
12
|
+
} | undefined>;
|
|
7
13
|
//# sourceMappingURL=LiveStoreContext.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStoreContext.d.ts","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"LiveStoreContext.d.ts","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,uBAAuB,EAAE,MAAM,sBAAsB,CAAA;AACnE,OAAO,KAAK,MAAM,OAAO,CAAA;AAEzB,OAAO,KAAK,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAA;AAC/D,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,eAAe,CAAA;AAE7C,MAAM,MAAM,QAAQ,GAAG;IACrB,QAAQ,EAAE,OAAO,QAAQ,CAAA;IACzB,iBAAiB,EAAE,OAAO,iBAAiB,CAAA;CAC5C,CAAA;AAED,eAAO,MAAM,gBAAgB;WAClB,SAAS;WAAS,uBAAuB,CAAC,OAAO,CAAC,GAAG,QAAQ;cAC5D,CAAA"}
|
package/dist/LiveStoreContext.js
CHANGED
|
@@ -1,13 +1,3 @@
|
|
|
1
|
-
import React
|
|
1
|
+
import React from 'react';
|
|
2
2
|
export const LiveStoreContext = React.createContext(undefined);
|
|
3
|
-
export const useStore = () => {
|
|
4
|
-
const storeContext = useContext(LiveStoreContext);
|
|
5
|
-
if (storeContext === undefined) {
|
|
6
|
-
throw new Error(`useStore can only be used inside StoreContext.Provider`);
|
|
7
|
-
}
|
|
8
|
-
if (storeContext.stage !== 'running') {
|
|
9
|
-
throw new Error(`useStore can only be used after the store is running`);
|
|
10
|
-
}
|
|
11
|
-
return storeContext;
|
|
12
|
-
};
|
|
13
3
|
//# sourceMappingURL=LiveStoreContext.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStoreContext.js","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"LiveStoreContext.js","sourceRoot":"","sources":["../src/LiveStoreContext.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,MAAM,OAAO,CAAA;AAUzB,MAAM,CAAC,MAAM,gBAAgB,GAAG,KAAK,CAAC,aAAa,CAEjD,SAAS,CAAC,CAAA"}
|
|
@@ -1,13 +1,14 @@
|
|
|
1
|
-
import type { Adapter, BootStatus, IntentionalShutdownCause } from '@livestore/common';
|
|
1
|
+
import type { Adapter, BootStatus, IntentionalShutdownCause, MigrationsReport } from '@livestore/common';
|
|
2
2
|
import { UnexpectedError } from '@livestore/common';
|
|
3
3
|
import type { LiveStoreSchema } from '@livestore/common/schema';
|
|
4
|
-
import type {
|
|
5
|
-
import {
|
|
4
|
+
import type { OtelOptions, Store } from '@livestore/livestore';
|
|
5
|
+
import { StoreInterrupted } from '@livestore/livestore';
|
|
6
6
|
import type { OtelTracer } from '@livestore/utils/effect';
|
|
7
|
-
import { Effect } from '@livestore/utils/effect';
|
|
7
|
+
import { Effect, Schema } from '@livestore/utils/effect';
|
|
8
8
|
import type * as otel from '@opentelemetry/api';
|
|
9
9
|
import type { ReactElement, ReactNode } from 'react';
|
|
10
|
-
|
|
10
|
+
import React from 'react';
|
|
11
|
+
export interface LiveStoreProviderProps {
|
|
11
12
|
schema: LiveStoreSchema;
|
|
12
13
|
/**
|
|
13
14
|
* The `storeId` can be used to isolate multiple stores from each other.
|
|
@@ -20,15 +21,17 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
|
20
21
|
* @default 'default'
|
|
21
22
|
*/
|
|
22
23
|
storeId?: string;
|
|
23
|
-
boot?: (store: Store<
|
|
24
|
-
|
|
24
|
+
boot?: (store: Store<LiveStoreSchema>, ctx: {
|
|
25
|
+
migrationsReport: MigrationsReport;
|
|
26
|
+
parentSpan: otel.Span;
|
|
27
|
+
}) => void | Promise<void> | Effect.Effect<void, unknown, OtelTracer.OtelTracer>;
|
|
25
28
|
otelOptions?: Partial<OtelOptions>;
|
|
26
|
-
renderLoading
|
|
29
|
+
renderLoading?: (status: BootStatus) => ReactElement;
|
|
27
30
|
renderError?: (error: UnexpectedError | unknown) => ReactElement;
|
|
28
|
-
renderShutdown?: (cause: IntentionalShutdownCause |
|
|
31
|
+
renderShutdown?: (cause: IntentionalShutdownCause | StoreInterrupted) => ReactElement;
|
|
29
32
|
adapter: Adapter;
|
|
30
33
|
/**
|
|
31
|
-
* In order for LiveStore to apply multiple
|
|
34
|
+
* In order for LiveStore to apply multiple events in a single render,
|
|
32
35
|
* you need to pass the `batchUpdates` function from either `react-dom` or `react-native`.
|
|
33
36
|
*
|
|
34
37
|
* ```ts
|
|
@@ -42,9 +45,24 @@ interface LiveStoreProviderProps<GraphQLContext extends BaseGraphQLContext> {
|
|
|
42
45
|
batchUpdates: (run: () => void) => void;
|
|
43
46
|
disableDevtools?: boolean;
|
|
44
47
|
signal?: AbortSignal;
|
|
48
|
+
/**
|
|
49
|
+
* Currently only used in the web adapter:
|
|
50
|
+
* If true, registers a beforeunload event listener to confirm unsaved changes.
|
|
51
|
+
*
|
|
52
|
+
* @default true
|
|
53
|
+
*/
|
|
54
|
+
confirmUnsavedChanges?: boolean;
|
|
55
|
+
/**
|
|
56
|
+
* Payload that will be passed to the sync backend when connecting
|
|
57
|
+
*
|
|
58
|
+
* @default undefined
|
|
59
|
+
*/
|
|
60
|
+
syncPayload?: Schema.JsonValue;
|
|
61
|
+
debug?: {
|
|
62
|
+
instanceId?: string;
|
|
63
|
+
};
|
|
45
64
|
}
|
|
46
|
-
export declare const LiveStoreProvider:
|
|
65
|
+
export declare const LiveStoreProvider: ({ renderLoading, renderError, renderShutdown, otelOptions, children, schema, storeId, boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges, syncPayload, debug, }: LiveStoreProviderProps & {
|
|
47
66
|
children?: ReactNode;
|
|
48
|
-
}) =>
|
|
49
|
-
export {};
|
|
67
|
+
}) => React.ReactElement;
|
|
50
68
|
//# sourceMappingURL=LiveStoreProvider.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStoreProvider.d.ts","sourceRoot":"","sources":["../src/LiveStoreProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"LiveStoreProvider.d.ts","sourceRoot":"","sources":["../src/LiveStoreProvider.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE,wBAAwB,EAAE,gBAAgB,EAAE,MAAM,mBAAmB,CAAA;AACxG,OAAO,EAAe,eAAe,EAAE,MAAM,mBAAmB,CAAA;AAChE,OAAO,KAAK,EAAE,eAAe,EAAE,MAAM,0BAA0B,CAAA;AAC/D,OAAO,KAAK,EAGV,WAAW,EAEX,KAAK,EACN,MAAM,sBAAsB,CAAA;AAC7B,OAAO,EAAqC,gBAAgB,EAAE,MAAM,sBAAsB,CAAA;AAE1F,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,yBAAyB,CAAA;AACzD,OAAO,EAGL,MAAM,EAKN,MAAM,EAGP,MAAM,yBAAyB,CAAA;AAChC,OAAO,KAAK,KAAK,IAAI,MAAM,oBAAoB,CAAA;AAC/C,OAAO,KAAK,EAAE,YAAY,EAAE,SAAS,EAAE,MAAM,OAAO,CAAA;AACpD,OAAO,KAAK,MAAM,OAAO,CAAA;AAIzB,MAAM,WAAW,sBAAsB;IACrC,MAAM,EAAE,eAAe,CAAA;IACvB;;;;;;;;;OASG;IACH,OAAO,CAAC,EAAE,MAAM,CAAA;IAChB,IAAI,CAAC,EAAE,CACL,KAAK,EAAE,KAAK,CAAC,eAAe,CAAC,EAC7B,GAAG,EAAE;QAAE,gBAAgB,EAAE,gBAAgB,CAAC;QAAC,UAAU,EAAE,IAAI,CAAC,IAAI,CAAA;KAAE,KAC/D,IAAI,GAAG,OAAO,CAAC,IAAI,CAAC,GAAG,MAAM,CAAC,MAAM,CAAC,IAAI,EAAE,OAAO,EAAE,UAAU,CAAC,UAAU,CAAC,CAAA;IAC/E,WAAW,CAAC,EAAE,OAAO,CAAC,WAAW,CAAC,CAAA;IAClC,aAAa,CAAC,EAAE,CAAC,MAAM,EAAE,UAAU,KAAK,YAAY,CAAA;IACpD,WAAW,CAAC,EAAE,CAAC,KAAK,EAAE,eAAe,GAAG,OAAO,KAAK,YAAY,CAAA;IAChE,cAAc,CAAC,EAAE,CAAC,KAAK,EAAE,wBAAwB,GAAG,gBAAgB,KAAK,YAAY,CAAA;IACrF,OAAO,EAAE,OAAO,CAAA;IAChB;;;;;;;;;;;OAWG;IACH,YAAY,EAAE,CAAC,GAAG,EAAE,MAAM,IAAI,KAAK,IAAI,CAAA;IACvC,eAAe,CAAC,EAAE,OAAO,CAAA;IACzB,MAAM,CAAC,EAAE,WAAW,CAAA;IACpB;;;;;OAKG;IACH,qBAAqB,CAAC,EAAE,OAAO,CAAA;IAC/B;;;;OAIG;IACH,WAAW,CAAC,EAAE,MAAM,CAAC,SAAS,CAAA;IAC9B,KAAK,CAAC,EAAE;QACN,UAAU,CAAC,EAAE,MAAM,CAAA;KACpB,CAAA;CACF;AAyBD,eAAO,MAAM,iBAAiB,GAAI,0LAgB/B,sBAAsB,GAAG;IAAE,QAAQ,CAAC,EAAE,SAAS,CAAA;CAAE,KAAG,KAAK,CAAC,YAkC5D,CAAA"}
|
|
@@ -1,35 +1,43 @@
|
|
|
1
1
|
import { provideOtel, UnexpectedError } from '@livestore/common';
|
|
2
|
-
import { createStore,
|
|
3
|
-
import { errorToString } from '@livestore/utils';
|
|
4
|
-
import { Deferred, Effect, Exit, Logger, LogLevel, Schema, Scope } from '@livestore/utils/effect';
|
|
2
|
+
import { createStore, makeShutdownDeferred, StoreInterrupted } from '@livestore/livestore';
|
|
3
|
+
import { errorToString, IS_REACT_NATIVE, LS_DEV } from '@livestore/utils';
|
|
4
|
+
import { Cause, Deferred, Effect, Exit, identity, Logger, LogLevel, Schema, Scope, TaskTracing, } from '@livestore/utils/effect';
|
|
5
5
|
import React from 'react';
|
|
6
6
|
import { LiveStoreContext } from './LiveStoreContext.js';
|
|
7
|
-
const defaultRenderError = (error) => (React.createElement(React.Fragment, null, Schema.is(UnexpectedError)(error) ? error.toString() : errorToString(error))
|
|
7
|
+
const defaultRenderError = (error) => IS_REACT_NATIVE ? React.createElement(React.Fragment, null) : React.createElement(React.Fragment, null, Schema.is(UnexpectedError)(error) ? error.toString() : errorToString(error));
|
|
8
8
|
const defaultRenderShutdown = (cause) => {
|
|
9
|
-
const reason = cause._tag === 'LiveStore.
|
|
10
|
-
?
|
|
9
|
+
const reason = cause._tag === 'LiveStore.StoreInterrupted'
|
|
10
|
+
? `interrupted due to: ${cause.reason}`
|
|
11
11
|
: cause.reason === 'devtools-import'
|
|
12
12
|
? 'devtools import'
|
|
13
13
|
: cause.reason === 'devtools-reset'
|
|
14
14
|
? 'devtools reset'
|
|
15
|
-
: cause.reason === '
|
|
16
|
-
? '
|
|
17
|
-
:
|
|
18
|
-
|
|
15
|
+
: cause.reason === 'adapter-reset'
|
|
16
|
+
? 'adapter reset'
|
|
17
|
+
: cause.reason === 'manual'
|
|
18
|
+
? 'manual shutdown'
|
|
19
|
+
: 'unknown reason';
|
|
20
|
+
return IS_REACT_NATIVE ? React.createElement(React.Fragment, null) : React.createElement(React.Fragment, null,
|
|
19
21
|
"LiveStore Shutdown due to ",
|
|
20
22
|
reason);
|
|
21
23
|
};
|
|
22
|
-
|
|
24
|
+
const defaultRenderLoading = (status) => IS_REACT_NATIVE ? React.createElement(React.Fragment, null) : React.createElement(React.Fragment, null,
|
|
25
|
+
"LiveStore is loading (",
|
|
26
|
+
status.stage,
|
|
27
|
+
")...");
|
|
28
|
+
export const LiveStoreProvider = ({ renderLoading = defaultRenderLoading, renderError = defaultRenderError, renderShutdown = defaultRenderShutdown, otelOptions, children, schema, storeId = 'default', boot, adapter, batchUpdates, disableDevtools, signal, confirmUnsavedChanges = true, syncPayload, debug, }) => {
|
|
23
29
|
const storeCtx = useCreateStore({
|
|
24
30
|
storeId,
|
|
25
31
|
schema,
|
|
26
|
-
graphQLOptions,
|
|
27
32
|
otelOptions,
|
|
28
33
|
boot,
|
|
29
34
|
adapter,
|
|
30
35
|
batchUpdates,
|
|
31
36
|
disableDevtools,
|
|
32
37
|
signal,
|
|
38
|
+
confirmUnsavedChanges,
|
|
39
|
+
syncPayload,
|
|
40
|
+
debug,
|
|
33
41
|
});
|
|
34
42
|
if (storeCtx.stage === 'error') {
|
|
35
43
|
return renderError(storeCtx.error);
|
|
@@ -44,65 +52,81 @@ export const LiveStoreProvider = ({ renderLoading, renderError = defaultRenderEr
|
|
|
44
52
|
if (Object.keys(globalThis.__debugLiveStore).length === 0) {
|
|
45
53
|
globalThis.__debugLiveStore['_'] = storeCtx.store;
|
|
46
54
|
}
|
|
47
|
-
globalThis.__debugLiveStore[storeId] = storeCtx.store;
|
|
55
|
+
globalThis.__debugLiveStore[debug?.instanceId ?? storeId] = storeCtx.store;
|
|
48
56
|
return React.createElement(LiveStoreContext.Provider, { value: storeCtx }, children);
|
|
49
57
|
};
|
|
50
|
-
const
|
|
51
|
-
const withSemaphore = (storeId) => {
|
|
52
|
-
let semaphore = semaphoreMap.get(storeId);
|
|
53
|
-
if (!semaphore) {
|
|
54
|
-
semaphore = Effect.makeSemaphore(1).pipe(Effect.runSync);
|
|
55
|
-
semaphoreMap.set(storeId, semaphore);
|
|
56
|
-
}
|
|
57
|
-
return semaphore.withPermits(1);
|
|
58
|
-
};
|
|
59
|
-
const useCreateStore = ({ schema, storeId, graphQLOptions, otelOptions, boot, adapter, batchUpdates, disableDevtools, reactivityGraph, signal, }) => {
|
|
58
|
+
const useCreateStore = ({ schema, storeId, otelOptions, boot, adapter, batchUpdates, disableDevtools, signal, context, params, confirmUnsavedChanges, syncPayload, debug, }) => {
|
|
60
59
|
const [_, rerender] = React.useState(0);
|
|
61
60
|
const ctxValueRef = React.useRef({
|
|
62
61
|
value: { stage: 'loading' },
|
|
63
62
|
componentScope: undefined,
|
|
64
63
|
shutdownDeferred: undefined,
|
|
64
|
+
previousShutdownDeferred: undefined,
|
|
65
65
|
counter: 0,
|
|
66
66
|
});
|
|
67
|
+
const debugInstanceId = debug?.instanceId;
|
|
67
68
|
// console.debug(`useCreateStore (${ctxValueRef.current.counter})`, ctxValueRef.current.value.stage)
|
|
68
69
|
const inputPropsCacheRef = React.useRef({
|
|
69
70
|
schema,
|
|
70
|
-
graphQLOptions,
|
|
71
71
|
otelOptions,
|
|
72
72
|
boot,
|
|
73
73
|
adapter,
|
|
74
74
|
batchUpdates,
|
|
75
75
|
disableDevtools,
|
|
76
|
-
reactivityGraph,
|
|
77
76
|
signal,
|
|
77
|
+
context,
|
|
78
|
+
params,
|
|
79
|
+
confirmUnsavedChanges,
|
|
80
|
+
syncPayload,
|
|
81
|
+
debugInstanceId,
|
|
78
82
|
});
|
|
79
83
|
const interrupt = (componentScope, shutdownDeferred, error) => Effect.gen(function* () {
|
|
80
84
|
// console.log('[@livestore/livestore/react] interupting', error)
|
|
81
85
|
yield* Scope.close(componentScope, Exit.fail(error));
|
|
82
86
|
yield* Deferred.fail(shutdownDeferred, error);
|
|
83
87
|
}).pipe(Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] interupting', cause)), Effect.runFork);
|
|
84
|
-
|
|
85
|
-
inputPropsCacheRef.current.
|
|
86
|
-
inputPropsCacheRef.current.otelOptions !== otelOptions
|
|
87
|
-
inputPropsCacheRef.current.boot !== boot
|
|
88
|
-
inputPropsCacheRef.current.adapter !== adapter
|
|
89
|
-
inputPropsCacheRef.current.batchUpdates !== batchUpdates
|
|
90
|
-
inputPropsCacheRef.current.disableDevtools !== disableDevtools
|
|
91
|
-
inputPropsCacheRef.current.
|
|
92
|
-
inputPropsCacheRef.current.
|
|
88
|
+
const inputPropChanges = {
|
|
89
|
+
schema: inputPropsCacheRef.current.schema !== schema,
|
|
90
|
+
otelOptions: inputPropsCacheRef.current.otelOptions !== otelOptions,
|
|
91
|
+
boot: inputPropsCacheRef.current.boot !== boot,
|
|
92
|
+
adapter: inputPropsCacheRef.current.adapter !== adapter,
|
|
93
|
+
batchUpdates: inputPropsCacheRef.current.batchUpdates !== batchUpdates,
|
|
94
|
+
disableDevtools: inputPropsCacheRef.current.disableDevtools !== disableDevtools,
|
|
95
|
+
signal: inputPropsCacheRef.current.signal !== signal,
|
|
96
|
+
context: inputPropsCacheRef.current.context !== context,
|
|
97
|
+
params: inputPropsCacheRef.current.params !== params,
|
|
98
|
+
confirmUnsavedChanges: inputPropsCacheRef.current.confirmUnsavedChanges !== confirmUnsavedChanges,
|
|
99
|
+
syncPayload: inputPropsCacheRef.current.syncPayload !== syncPayload,
|
|
100
|
+
debugInstanceId: inputPropsCacheRef.current.debugInstanceId !== debugInstanceId,
|
|
101
|
+
};
|
|
102
|
+
if (inputPropChanges.schema ||
|
|
103
|
+
inputPropChanges.otelOptions ||
|
|
104
|
+
inputPropChanges.boot ||
|
|
105
|
+
inputPropChanges.adapter ||
|
|
106
|
+
inputPropChanges.batchUpdates ||
|
|
107
|
+
inputPropChanges.disableDevtools ||
|
|
108
|
+
inputPropChanges.signal ||
|
|
109
|
+
inputPropChanges.context ||
|
|
110
|
+
inputPropChanges.params ||
|
|
111
|
+
inputPropChanges.confirmUnsavedChanges ||
|
|
112
|
+
inputPropChanges.syncPayload) {
|
|
93
113
|
inputPropsCacheRef.current = {
|
|
94
114
|
schema,
|
|
95
|
-
graphQLOptions,
|
|
96
115
|
otelOptions,
|
|
97
116
|
boot,
|
|
98
117
|
adapter,
|
|
99
118
|
batchUpdates,
|
|
100
119
|
disableDevtools,
|
|
101
|
-
reactivityGraph,
|
|
102
120
|
signal,
|
|
121
|
+
context,
|
|
122
|
+
params,
|
|
123
|
+
confirmUnsavedChanges,
|
|
124
|
+
syncPayload,
|
|
125
|
+
debugInstanceId,
|
|
103
126
|
};
|
|
104
127
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
|
105
|
-
|
|
128
|
+
const changedInputProps = Object.keys(inputPropChanges).filter((key) => inputPropChanges[key]);
|
|
129
|
+
interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted({ reason: `re-rendering due to changed input props: ${changedInputProps.join(', ')}` }));
|
|
106
130
|
ctxValueRef.current.componentScope = undefined;
|
|
107
131
|
ctxValueRef.current.shutdownDeferred = undefined;
|
|
108
132
|
}
|
|
@@ -110,6 +134,7 @@ const useCreateStore = ({ schema, storeId, graphQLOptions, otelOptions, boot, ad
|
|
|
110
134
|
value: { stage: 'loading' },
|
|
111
135
|
componentScope: undefined,
|
|
112
136
|
shutdownDeferred: undefined,
|
|
137
|
+
previousShutdownDeferred: ctxValueRef.current.shutdownDeferred,
|
|
113
138
|
counter: ctxValueRef.current.counter + 1,
|
|
114
139
|
};
|
|
115
140
|
}
|
|
@@ -125,60 +150,68 @@ const useCreateStore = ({ schema, storeId, graphQLOptions, otelOptions, boot, ad
|
|
|
125
150
|
if (ctxValueRef.current.componentScope !== undefined &&
|
|
126
151
|
ctxValueRef.current.shutdownDeferred !== undefined &&
|
|
127
152
|
ctxValueRef.current.counter === counter) {
|
|
128
|
-
interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new
|
|
153
|
+
interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted({ reason: 'Aborted via provided AbortController' }));
|
|
129
154
|
ctxValueRef.current.componentScope = undefined;
|
|
130
155
|
ctxValueRef.current.shutdownDeferred = undefined;
|
|
131
156
|
}
|
|
132
157
|
});
|
|
133
|
-
Effect.gen(function* () {
|
|
134
|
-
|
|
135
|
-
|
|
158
|
+
const cancel = Effect.gen(function* () {
|
|
159
|
+
// Wait for the previous store to fully shutdown before creating a new one
|
|
160
|
+
if (ctxValueRef.current.previousShutdownDeferred) {
|
|
161
|
+
yield* Deferred.await(ctxValueRef.current.previousShutdownDeferred);
|
|
162
|
+
}
|
|
163
|
+
const componentScope = yield* Scope.make().pipe(Effect.acquireRelease(Scope.close));
|
|
164
|
+
const shutdownDeferred = yield* makeShutdownDeferred;
|
|
136
165
|
ctxValueRef.current.componentScope = componentScope;
|
|
137
166
|
ctxValueRef.current.shutdownDeferred = shutdownDeferred;
|
|
138
167
|
yield* Effect.gen(function* () {
|
|
139
168
|
const store = yield* createStore({
|
|
140
169
|
schema,
|
|
141
170
|
storeId,
|
|
142
|
-
graphQLOptions,
|
|
143
171
|
boot,
|
|
144
172
|
adapter,
|
|
145
|
-
reactivityGraph,
|
|
146
173
|
batchUpdates,
|
|
147
174
|
disableDevtools,
|
|
148
175
|
shutdownDeferred,
|
|
176
|
+
context,
|
|
177
|
+
params,
|
|
178
|
+
confirmUnsavedChanges,
|
|
179
|
+
syncPayload,
|
|
149
180
|
onBootStatus: (status) => {
|
|
150
181
|
if (ctxValueRef.current.value.stage === 'running' || ctxValueRef.current.value.stage === 'error')
|
|
151
182
|
return;
|
|
183
|
+
// NOTE sometimes when status come in in rapid succession, only the last value will be rendered by React
|
|
152
184
|
setContextValue(status);
|
|
153
185
|
},
|
|
186
|
+
debug: { instanceId: debugInstanceId },
|
|
154
187
|
}).pipe(Effect.tapErrorCause((cause) => Deferred.failCause(shutdownDeferred, cause)));
|
|
155
188
|
setContextValue({ stage: 'running', store });
|
|
156
189
|
}).pipe(Scope.extend(componentScope), Effect.forkIn(componentScope));
|
|
157
190
|
const shutdownContext = (cause) => Effect.sync(() => setContextValue({ stage: 'shutdown', cause }));
|
|
158
|
-
yield* Deferred.await(shutdownDeferred).pipe(Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', cause)), Effect.catchTag('LiveStore.IntentionalShutdownCause', (cause) => shutdownContext(cause)), Effect.catchTag('LiveStore.
|
|
159
|
-
}).pipe(Effect.scoped,
|
|
160
|
-
// NOTE we're running the code above in a semaphore to make sure a previous store is always fully
|
|
161
|
-
// shutdown before a new one is created - especially when shutdown logic is async. You can't trust `React.useEffect`.
|
|
162
|
-
// Thank you to Mattia Manzati for this idea.
|
|
163
|
-
withSemaphore(storeId), Effect.tapCauseLogPretty, provideOtel({ parentSpanContext: otelOptions?.rootSpanContext, otelTracer: otelOptions?.tracer }), Effect.annotateLogs({ thread: 'window' }), Effect.provide(Logger.prettyWithThread('window')), Logger.withMinimumLogLevel(LogLevel.Debug), Effect.runFork);
|
|
191
|
+
yield* Deferred.await(shutdownDeferred).pipe(Effect.tapErrorCause((cause) => Effect.logDebug('[@livestore/livestore/react] shutdown', Cause.pretty(cause))), Effect.catchTag('LiveStore.IntentionalShutdownCause', (cause) => shutdownContext(cause)), Effect.catchTag('LiveStore.StoreInterrupted', (cause) => shutdownContext(cause)), Effect.tapError((error) => Effect.sync(() => setContextValue({ stage: 'error', error }))), Effect.tapDefect((defect) => Effect.sync(() => setContextValue({ stage: 'error', error: defect }))), Effect.exit);
|
|
192
|
+
}).pipe(Effect.scoped, Effect.withSpan('@livestore/react:useCreateStore'), LS_DEV ? TaskTracing.withAsyncTaggingTracing((name) => console.createTask(name)) : identity, provideOtel({ parentSpanContext: otelOptions?.rootSpanContext, otelTracer: otelOptions?.tracer }), Effect.tapCauseLogPretty, Effect.annotateLogs({ thread: 'window' }), Effect.provide(Logger.prettyWithThread('window')), Logger.withMinimumLogLevel(LogLevel.Debug), Effect.runCallback);
|
|
164
193
|
return () => {
|
|
194
|
+
cancel();
|
|
165
195
|
if (ctxValueRef.current.componentScope !== undefined && ctxValueRef.current.shutdownDeferred !== undefined) {
|
|
166
|
-
interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted());
|
|
196
|
+
interrupt(ctxValueRef.current.componentScope, ctxValueRef.current.shutdownDeferred, new StoreInterrupted({ reason: 'unmounting component' }));
|
|
167
197
|
ctxValueRef.current.componentScope = undefined;
|
|
168
198
|
ctxValueRef.current.shutdownDeferred = undefined;
|
|
169
199
|
}
|
|
170
200
|
};
|
|
171
201
|
}, [
|
|
172
202
|
schema,
|
|
173
|
-
graphQLOptions,
|
|
174
203
|
otelOptions,
|
|
175
204
|
boot,
|
|
176
205
|
adapter,
|
|
177
206
|
batchUpdates,
|
|
178
207
|
disableDevtools,
|
|
179
208
|
signal,
|
|
180
|
-
reactivityGraph,
|
|
181
209
|
storeId,
|
|
210
|
+
context,
|
|
211
|
+
params,
|
|
212
|
+
confirmUnsavedChanges,
|
|
213
|
+
syncPayload,
|
|
214
|
+
debugInstanceId,
|
|
182
215
|
]);
|
|
183
216
|
return ctxValueRef.current.value;
|
|
184
217
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"LiveStoreProvider.js","sourceRoot":"","sources":["../src/LiveStoreProvider.tsx"],"names":[],"mappings":"AACA,OAAO,EAAE,WAAW,EAAE,eAAe,EAAE,MAAM,mBAAmB,CAAA;
|
|
1
|
+
{"version":3,"file":"LiveStoreProvider.js","sourceRoot":"","sources":["../src/LiveStoreProvider.tsx"],"names":[],"mappings":"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"}
|
|
@@ -1,46 +1,49 @@
|
|
|
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 } 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
|
-
import { schema, tables } from './__tests__/fixture.js';
|
|
10
|
+
import { events, schema, tables } from './__tests__/fixture.js';
|
|
11
11
|
import { LiveStoreProvider } from './LiveStoreProvider.js';
|
|
12
12
|
import * as LiveStoreReact from './mod.js';
|
|
13
|
-
describe('LiveStoreProvider', () => {
|
|
13
|
+
describe.each([true, false])('LiveStoreProvider (strictMode: %s)', (strictMode) => {
|
|
14
|
+
const WithStrictMode = strictMode ? React.StrictMode : React.Fragment;
|
|
14
15
|
it('simple', async () => {
|
|
15
16
|
let appRenderCount = 0;
|
|
16
|
-
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.
|
|
17
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) });
|
|
17
18
|
const App = () => {
|
|
18
19
|
appRenderCount++;
|
|
19
|
-
const
|
|
20
|
+
const { store } = LiveStoreReact.useStore();
|
|
21
|
+
const todos = store.useQuery(allTodos$);
|
|
20
22
|
return React.createElement("div", null, JSON.stringify(todos));
|
|
21
23
|
};
|
|
22
24
|
const abortController = new AbortController();
|
|
23
25
|
const Root = ({ forceUpdate }) => {
|
|
24
|
-
const bootCb = React.useCallback((store) => store.
|
|
26
|
+
const bootCb = React.useCallback((store) => store.commit(rawSqlEvent({
|
|
25
27
|
sql: sql `INSERT OR IGNORE INTO todos (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
|
|
26
28
|
})), []);
|
|
27
29
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
28
30
|
const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate]);
|
|
29
|
-
return (React.createElement(
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
31
|
+
return (React.createElement(WithStrictMode, null,
|
|
32
|
+
React.createElement(LiveStoreProvider, { schema: schema, renderLoading: (status) => React.createElement("div", null,
|
|
33
|
+
"Loading LiveStore: ",
|
|
34
|
+
status.stage), adapter: adapterMemo, boot: bootCb, signal: abortController.signal, batchUpdates: batchUpdates },
|
|
35
|
+
React.createElement(App, null))));
|
|
33
36
|
};
|
|
34
|
-
const { rerender } = render(React.createElement(Root, { forceUpdate: 1 }));
|
|
37
|
+
const { rerender } = ReactTesting.render(React.createElement(Root, { forceUpdate: 1 }));
|
|
35
38
|
expect(appRenderCount).toBe(0);
|
|
36
|
-
await waitForElementToBeRemoved(() => screen.getByText((_) => _.startsWith('Loading LiveStore')));
|
|
37
|
-
expect(appRenderCount).toBe(1);
|
|
39
|
+
await ReactTesting.waitForElementToBeRemoved(() => ReactTesting.screen.getByText((_) => _.startsWith('Loading LiveStore')));
|
|
40
|
+
expect(appRenderCount).toBe(strictMode ? 2 : 1);
|
|
38
41
|
rerender(React.createElement(Root, { forceUpdate: 2 }));
|
|
39
|
-
await waitFor(() => screen.getByText('Loading LiveStore: loading'));
|
|
40
|
-
await waitFor(() => screen.getByText((_) => _.includes('buy milk')));
|
|
41
|
-
expect(appRenderCount).toBe(2);
|
|
42
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('Loading LiveStore: loading'));
|
|
43
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText((_) => _.includes('buy milk')));
|
|
44
|
+
expect(appRenderCount).toBe(strictMode ? 4 : 2);
|
|
42
45
|
abortController.abort();
|
|
43
|
-
await waitFor(() => screen.getByText('LiveStore Shutdown due to
|
|
46
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('LiveStore Shutdown due to interrupted', { exact: false }));
|
|
44
47
|
});
|
|
45
48
|
// TODO test aborting during boot
|
|
46
49
|
it('error during boot', async () => {
|
|
@@ -50,19 +53,88 @@ describe('LiveStoreProvider', () => {
|
|
|
50
53
|
return React.createElement("div", null, "hello world");
|
|
51
54
|
};
|
|
52
55
|
const Root = ({ forceUpdate }) => {
|
|
53
|
-
const bootCb = React.useCallback((store) => store.
|
|
56
|
+
const bootCb = React.useCallback((store) => store.commit(rawSqlEvent({
|
|
54
57
|
sql: sql `INSERT OR IGNORE INTO todos_missing_table (id, text, completed) VALUES ('t1', 'buy milk', 0)`,
|
|
55
58
|
})), []);
|
|
56
59
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
57
60
|
const adapterMemo = React.useMemo(() => makeInMemoryAdapter(), [forceUpdate]);
|
|
58
|
-
return (React.createElement(
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
61
|
+
return (React.createElement(WithStrictMode, null,
|
|
62
|
+
React.createElement(LiveStoreProvider, { schema: schema, renderLoading: (status) => React.createElement("div", null,
|
|
63
|
+
"Loading LiveStore: ",
|
|
64
|
+
status.stage), adapter: adapterMemo, boot: bootCb, batchUpdates: batchUpdates },
|
|
65
|
+
React.createElement(App, null))));
|
|
62
66
|
};
|
|
63
|
-
render(React.createElement(Root, { forceUpdate: 1 }));
|
|
67
|
+
ReactTesting.render(React.createElement(Root, { forceUpdate: 1 }));
|
|
64
68
|
expect(appRenderCount).toBe(0);
|
|
65
|
-
await waitFor(() => screen.getByText((_) => _.startsWith('LiveStore.UnexpectedError')));
|
|
69
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText((_) => _.startsWith('LiveStore.UnexpectedError')));
|
|
66
70
|
});
|
|
71
|
+
it('unmounts when store is shutdown', async () => {
|
|
72
|
+
let appRenderCount = 0;
|
|
73
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) });
|
|
74
|
+
const shutdownDeferred = Promise.withResolvers();
|
|
75
|
+
const App = () => {
|
|
76
|
+
appRenderCount++;
|
|
77
|
+
const { store } = LiveStoreReact.useStore();
|
|
78
|
+
React.useEffect(() => {
|
|
79
|
+
shutdownDeferred.promise.then(() => {
|
|
80
|
+
console.log('shutdown');
|
|
81
|
+
return store.shutdown();
|
|
82
|
+
});
|
|
83
|
+
}, [store]);
|
|
84
|
+
const todos = store.useQuery(allTodos$);
|
|
85
|
+
return React.createElement("div", null, JSON.stringify(todos));
|
|
86
|
+
};
|
|
87
|
+
const adapter = makeInMemoryAdapter();
|
|
88
|
+
const Root = () => {
|
|
89
|
+
return (React.createElement(WithStrictMode, null,
|
|
90
|
+
React.createElement(LiveStoreProvider, { schema: schema, renderLoading: (status) => React.createElement("div", null,
|
|
91
|
+
"Loading LiveStore: ",
|
|
92
|
+
status.stage), adapter: adapter, batchUpdates: batchUpdates },
|
|
93
|
+
React.createElement(App, null))));
|
|
94
|
+
};
|
|
95
|
+
ReactTesting.render(React.createElement(Root, null));
|
|
96
|
+
expect(appRenderCount).toBe(0);
|
|
97
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('[]'));
|
|
98
|
+
React.act(() => shutdownDeferred.resolve());
|
|
99
|
+
expect(appRenderCount).toBe(strictMode ? 2 : 1);
|
|
100
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByText('LiveStore Shutdown due to manual shutdown', { exact: false }));
|
|
101
|
+
});
|
|
102
|
+
});
|
|
103
|
+
it('should work two stores with the same storeId', async () => {
|
|
104
|
+
const allTodos$ = queryDb({ query: `select * from todos`, schema: Schema.Array(tables.todos.rowSchema) });
|
|
105
|
+
const appRenderCount = {
|
|
106
|
+
store1: 0,
|
|
107
|
+
store2: 0,
|
|
108
|
+
};
|
|
109
|
+
const App = () => {
|
|
110
|
+
const { store } = LiveStoreReact.useStore();
|
|
111
|
+
const instanceId = store.clientSession.debugInstanceId;
|
|
112
|
+
appRenderCount[instanceId]++;
|
|
113
|
+
const todos = store.useQuery(allTodos$);
|
|
114
|
+
return (React.createElement("div", { id: instanceId },
|
|
115
|
+
React.createElement("div", { role: "heading" }, instanceId),
|
|
116
|
+
React.createElement("div", { role: "content" }, JSON.stringify(todos)),
|
|
117
|
+
React.createElement("button", { onClick: () => store.commit(events.todoCreated({ id: 't1', text: 'buy milk', completed: false })) },
|
|
118
|
+
"create todo ",
|
|
119
|
+
instanceId)));
|
|
120
|
+
};
|
|
121
|
+
const Root = () => {
|
|
122
|
+
const storeId = 'fixed-store-id';
|
|
123
|
+
return (React.createElement("div", null,
|
|
124
|
+
React.createElement(LiveStoreProvider, { storeId: storeId, debug: { instanceId: 'store1' }, schema: schema, adapter: makeInMemoryAdapter(), batchUpdates: batchUpdates },
|
|
125
|
+
React.createElement(App, null)),
|
|
126
|
+
React.createElement(LiveStoreProvider, { storeId: storeId, debug: { instanceId: 'store2' }, schema: schema, adapter: makeInMemoryAdapter(), batchUpdates: batchUpdates },
|
|
127
|
+
React.createElement(App, null))));
|
|
128
|
+
};
|
|
129
|
+
const { container } = ReactTesting.render(React.createElement(Root, null));
|
|
130
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByRole('heading', { name: 'store1' }));
|
|
131
|
+
await ReactTesting.waitFor(() => ReactTesting.screen.getByRole('heading', { name: 'store2' }));
|
|
132
|
+
expect(appRenderCount.store1).toBe(1);
|
|
133
|
+
expect(appRenderCount.store2).toBe(1);
|
|
134
|
+
ReactTesting.fireEvent.click(ReactTesting.screen.getByText('create todo store1'));
|
|
135
|
+
expect(appRenderCount.store1).toBe(2);
|
|
136
|
+
expect(container.querySelector('#store1 > div[role="content"]')?.textContent).toBe('[{"id":"t1","text":"buy milk","completed":false}]');
|
|
137
|
+
expect(container.querySelector('#store2 > div[role="content"]')?.textContent).toBe('[]');
|
|
67
138
|
});
|
|
139
|
+
// TODO test that checks that there are no two exact same instances (i.e. same storeId, clientId, sessionId)
|
|
68
140
|
//# sourceMappingURL=LiveStoreProvider.test.js.map
|