@tanstack/solid-query-persist-client 5.0.0-beta.21
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE +21 -0
- package/build/dev.cjs +56 -0
- package/build/dev.js +50 -0
- package/build/index.cjs +56 -0
- package/build/index.d.cts +12 -0
- package/build/index.d.ts +12 -0
- package/build/index.js +50 -0
- package/package.json +67 -0
- package/src/PersistQueryClientProvider.tsx +52 -0
- package/src/__tests__/PersistQueryClientProvider.test.tsx +582 -0
- package/src/__tests__/utils.ts +18 -0
- package/src/index.ts +4 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021-present Tanner Linsley
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/build/dev.cjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var queryPersistClientCore = require('@tanstack/query-persist-client-core');
|
|
4
|
+
var web = require('solid-js/web');
|
|
5
|
+
var solidJs = require('solid-js');
|
|
6
|
+
var solidQuery = require('@tanstack/solid-query');
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
exports.PersistQueryClientProvider = (props) => {
|
|
10
|
+
const [isRestoring, setIsRestoring] = solidJs.createSignal(true);
|
|
11
|
+
let unsub;
|
|
12
|
+
solidJs.createComputed((cleanup) => {
|
|
13
|
+
cleanup?.();
|
|
14
|
+
let isStale = false;
|
|
15
|
+
setIsRestoring(true);
|
|
16
|
+
const [unsubscribe, promise] = queryPersistClientCore.persistQueryClient({
|
|
17
|
+
...props.persistOptions,
|
|
18
|
+
queryClient: props.client
|
|
19
|
+
});
|
|
20
|
+
promise.then(async () => {
|
|
21
|
+
if (isStale)
|
|
22
|
+
return;
|
|
23
|
+
try {
|
|
24
|
+
await props.onSuccess?.();
|
|
25
|
+
} finally {
|
|
26
|
+
setIsRestoring(false);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
unsub = () => {
|
|
30
|
+
isStale = true;
|
|
31
|
+
unsubscribe();
|
|
32
|
+
};
|
|
33
|
+
return unsub;
|
|
34
|
+
});
|
|
35
|
+
solidJs.onCleanup(() => unsub?.());
|
|
36
|
+
return web.createComponent(solidQuery.QueryClientProvider, {
|
|
37
|
+
get client() {
|
|
38
|
+
return props.client;
|
|
39
|
+
},
|
|
40
|
+
get children() {
|
|
41
|
+
return web.createComponent(solidQuery.IsRestoringProvider, {
|
|
42
|
+
value: isRestoring,
|
|
43
|
+
get children() {
|
|
44
|
+
return props.children;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
Object.keys(queryPersistClientCore).forEach(function (k) {
|
|
52
|
+
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: function () { return queryPersistClientCore[k]; }
|
|
55
|
+
});
|
|
56
|
+
});
|
package/build/dev.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { persistQueryClient } from '@tanstack/query-persist-client-core';
|
|
2
|
+
export * from '@tanstack/query-persist-client-core';
|
|
3
|
+
import { createComponent } from 'solid-js/web';
|
|
4
|
+
import { createSignal, createComputed, onCleanup } from 'solid-js';
|
|
5
|
+
import { QueryClientProvider, IsRestoringProvider } from '@tanstack/solid-query';
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
var PersistQueryClientProvider = (props) => {
|
|
9
|
+
const [isRestoring, setIsRestoring] = createSignal(true);
|
|
10
|
+
let unsub;
|
|
11
|
+
createComputed((cleanup) => {
|
|
12
|
+
cleanup?.();
|
|
13
|
+
let isStale = false;
|
|
14
|
+
setIsRestoring(true);
|
|
15
|
+
const [unsubscribe, promise] = persistQueryClient({
|
|
16
|
+
...props.persistOptions,
|
|
17
|
+
queryClient: props.client
|
|
18
|
+
});
|
|
19
|
+
promise.then(async () => {
|
|
20
|
+
if (isStale)
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
await props.onSuccess?.();
|
|
24
|
+
} finally {
|
|
25
|
+
setIsRestoring(false);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
unsub = () => {
|
|
29
|
+
isStale = true;
|
|
30
|
+
unsubscribe();
|
|
31
|
+
};
|
|
32
|
+
return unsub;
|
|
33
|
+
});
|
|
34
|
+
onCleanup(() => unsub?.());
|
|
35
|
+
return createComponent(QueryClientProvider, {
|
|
36
|
+
get client() {
|
|
37
|
+
return props.client;
|
|
38
|
+
},
|
|
39
|
+
get children() {
|
|
40
|
+
return createComponent(IsRestoringProvider, {
|
|
41
|
+
value: isRestoring,
|
|
42
|
+
get children() {
|
|
43
|
+
return props.children;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { PersistQueryClientProvider };
|
package/build/index.cjs
ADDED
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var queryPersistClientCore = require('@tanstack/query-persist-client-core');
|
|
4
|
+
var web = require('solid-js/web');
|
|
5
|
+
var solidJs = require('solid-js');
|
|
6
|
+
var solidQuery = require('@tanstack/solid-query');
|
|
7
|
+
|
|
8
|
+
// src/index.ts
|
|
9
|
+
exports.PersistQueryClientProvider = (props) => {
|
|
10
|
+
const [isRestoring, setIsRestoring] = solidJs.createSignal(true);
|
|
11
|
+
let unsub;
|
|
12
|
+
solidJs.createComputed((cleanup) => {
|
|
13
|
+
cleanup?.();
|
|
14
|
+
let isStale = false;
|
|
15
|
+
setIsRestoring(true);
|
|
16
|
+
const [unsubscribe, promise] = queryPersistClientCore.persistQueryClient({
|
|
17
|
+
...props.persistOptions,
|
|
18
|
+
queryClient: props.client
|
|
19
|
+
});
|
|
20
|
+
promise.then(async () => {
|
|
21
|
+
if (isStale)
|
|
22
|
+
return;
|
|
23
|
+
try {
|
|
24
|
+
await props.onSuccess?.();
|
|
25
|
+
} finally {
|
|
26
|
+
setIsRestoring(false);
|
|
27
|
+
}
|
|
28
|
+
});
|
|
29
|
+
unsub = () => {
|
|
30
|
+
isStale = true;
|
|
31
|
+
unsubscribe();
|
|
32
|
+
};
|
|
33
|
+
return unsub;
|
|
34
|
+
});
|
|
35
|
+
solidJs.onCleanup(() => unsub?.());
|
|
36
|
+
return web.createComponent(solidQuery.QueryClientProvider, {
|
|
37
|
+
get client() {
|
|
38
|
+
return props.client;
|
|
39
|
+
},
|
|
40
|
+
get children() {
|
|
41
|
+
return web.createComponent(solidQuery.IsRestoringProvider, {
|
|
42
|
+
value: isRestoring,
|
|
43
|
+
get children() {
|
|
44
|
+
return props.children;
|
|
45
|
+
}
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
});
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
Object.keys(queryPersistClientCore).forEach(function (k) {
|
|
52
|
+
if (k !== 'default' && !exports.hasOwnProperty(k)) Object.defineProperty(exports, k, {
|
|
53
|
+
enumerable: true,
|
|
54
|
+
get: function () { return queryPersistClientCore[k]; }
|
|
55
|
+
});
|
|
56
|
+
});
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PersistQueryClientOptions } from '@tanstack/query-persist-client-core';
|
|
2
|
+
export * from '@tanstack/query-persist-client-core';
|
|
3
|
+
import { QueryClientProviderProps } from '@tanstack/solid-query';
|
|
4
|
+
import { JSX } from 'solid-js';
|
|
5
|
+
|
|
6
|
+
type PersistQueryClientProviderProps = QueryClientProviderProps & {
|
|
7
|
+
persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>;
|
|
8
|
+
onSuccess?: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare const PersistQueryClientProvider: (props: PersistQueryClientProviderProps) => JSX.Element;
|
|
11
|
+
|
|
12
|
+
export { PersistQueryClientProvider, PersistQueryClientProviderProps };
|
package/build/index.d.ts
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
import { PersistQueryClientOptions } from '@tanstack/query-persist-client-core';
|
|
2
|
+
export * from '@tanstack/query-persist-client-core';
|
|
3
|
+
import { QueryClientProviderProps } from '@tanstack/solid-query';
|
|
4
|
+
import { JSX } from 'solid-js';
|
|
5
|
+
|
|
6
|
+
type PersistQueryClientProviderProps = QueryClientProviderProps & {
|
|
7
|
+
persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>;
|
|
8
|
+
onSuccess?: () => void;
|
|
9
|
+
};
|
|
10
|
+
declare const PersistQueryClientProvider: (props: PersistQueryClientProviderProps) => JSX.Element;
|
|
11
|
+
|
|
12
|
+
export { PersistQueryClientProvider, PersistQueryClientProviderProps };
|
package/build/index.js
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
import { persistQueryClient } from '@tanstack/query-persist-client-core';
|
|
2
|
+
export * from '@tanstack/query-persist-client-core';
|
|
3
|
+
import { createComponent } from 'solid-js/web';
|
|
4
|
+
import { createSignal, createComputed, onCleanup } from 'solid-js';
|
|
5
|
+
import { QueryClientProvider, IsRestoringProvider } from '@tanstack/solid-query';
|
|
6
|
+
|
|
7
|
+
// src/index.ts
|
|
8
|
+
var PersistQueryClientProvider = (props) => {
|
|
9
|
+
const [isRestoring, setIsRestoring] = createSignal(true);
|
|
10
|
+
let unsub;
|
|
11
|
+
createComputed((cleanup) => {
|
|
12
|
+
cleanup?.();
|
|
13
|
+
let isStale = false;
|
|
14
|
+
setIsRestoring(true);
|
|
15
|
+
const [unsubscribe, promise] = persistQueryClient({
|
|
16
|
+
...props.persistOptions,
|
|
17
|
+
queryClient: props.client
|
|
18
|
+
});
|
|
19
|
+
promise.then(async () => {
|
|
20
|
+
if (isStale)
|
|
21
|
+
return;
|
|
22
|
+
try {
|
|
23
|
+
await props.onSuccess?.();
|
|
24
|
+
} finally {
|
|
25
|
+
setIsRestoring(false);
|
|
26
|
+
}
|
|
27
|
+
});
|
|
28
|
+
unsub = () => {
|
|
29
|
+
isStale = true;
|
|
30
|
+
unsubscribe();
|
|
31
|
+
};
|
|
32
|
+
return unsub;
|
|
33
|
+
});
|
|
34
|
+
onCleanup(() => unsub?.());
|
|
35
|
+
return createComponent(QueryClientProvider, {
|
|
36
|
+
get client() {
|
|
37
|
+
return props.client;
|
|
38
|
+
},
|
|
39
|
+
get children() {
|
|
40
|
+
return createComponent(IsRestoringProvider, {
|
|
41
|
+
value: isRestoring,
|
|
42
|
+
get children() {
|
|
43
|
+
return props.children;
|
|
44
|
+
}
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
});
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
export { PersistQueryClientProvider };
|
package/package.json
ADDED
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@tanstack/solid-query-persist-client",
|
|
3
|
+
"version": "5.0.0-beta.21",
|
|
4
|
+
"description": "Solid.js bindings to work with persisters in TanStack/solid-query",
|
|
5
|
+
"author": "tannerlinsley",
|
|
6
|
+
"license": "MIT",
|
|
7
|
+
"repository": {
|
|
8
|
+
"type": "git",
|
|
9
|
+
"url": "https://github.com/TanStack/query.git",
|
|
10
|
+
"directory": "packages/solid-query-persist-client"
|
|
11
|
+
},
|
|
12
|
+
"homepage": "https://tanstack.com/query",
|
|
13
|
+
"funding": {
|
|
14
|
+
"type": "github",
|
|
15
|
+
"url": "https://github.com/sponsors/tannerlinsley"
|
|
16
|
+
},
|
|
17
|
+
"type": "module",
|
|
18
|
+
"types": "./build/index.d.ts",
|
|
19
|
+
"main": "./build/index.cjs",
|
|
20
|
+
"module": "./build/index.js",
|
|
21
|
+
"exports": {
|
|
22
|
+
"development": {
|
|
23
|
+
"import": {
|
|
24
|
+
"types": "./build/index.d.ts",
|
|
25
|
+
"default": "./build/dev.js"
|
|
26
|
+
},
|
|
27
|
+
"require": {
|
|
28
|
+
"types": "./build/index.d.cts",
|
|
29
|
+
"default": "./build/dev.cjs"
|
|
30
|
+
}
|
|
31
|
+
},
|
|
32
|
+
"import": {
|
|
33
|
+
"types": "./build/index.d.ts",
|
|
34
|
+
"default": "./build/index.js"
|
|
35
|
+
},
|
|
36
|
+
"require": {
|
|
37
|
+
"types": "./build/index.d.cts",
|
|
38
|
+
"default": "./build/index.cjs"
|
|
39
|
+
}
|
|
40
|
+
},
|
|
41
|
+
"sideEffects": false,
|
|
42
|
+
"files": [
|
|
43
|
+
"build",
|
|
44
|
+
"src"
|
|
45
|
+
],
|
|
46
|
+
"dependencies": {
|
|
47
|
+
"@tanstack/query-persist-client-core": "5.0.0-beta.20"
|
|
48
|
+
},
|
|
49
|
+
"devDependencies": {
|
|
50
|
+
"tsup-preset-solid": "^2.0.1",
|
|
51
|
+
"vite-plugin-solid": "^2.7.0",
|
|
52
|
+
"solid-js": "^1.7.8"
|
|
53
|
+
},
|
|
54
|
+
"peerDependencies": {
|
|
55
|
+
"solid-js": "^1.6.0",
|
|
56
|
+
"@tanstack/solid-query": "5.0.0-beta.21"
|
|
57
|
+
},
|
|
58
|
+
"scripts": {
|
|
59
|
+
"clean": "rimraf ./build && rimraf ./coverage",
|
|
60
|
+
"test:eslint": "eslint --ext .ts,.tsx ./src",
|
|
61
|
+
"test:types": "tsc",
|
|
62
|
+
"test:lib": "vitest run --coverage",
|
|
63
|
+
"test:lib:dev": "vitest watch --coverage",
|
|
64
|
+
"test:build": "publint --strict",
|
|
65
|
+
"build": "tsup"
|
|
66
|
+
}
|
|
67
|
+
}
|
|
@@ -0,0 +1,52 @@
|
|
|
1
|
+
import { persistQueryClient } from '@tanstack/query-persist-client-core'
|
|
2
|
+
import { createComputed, createSignal, onCleanup } from 'solid-js'
|
|
3
|
+
import { IsRestoringProvider, QueryClientProvider } from '@tanstack/solid-query'
|
|
4
|
+
import type { PersistQueryClientOptions } from '@tanstack/query-persist-client-core'
|
|
5
|
+
import type { QueryClientProviderProps } from '@tanstack/solid-query'
|
|
6
|
+
import type { JSX } from 'solid-js'
|
|
7
|
+
|
|
8
|
+
export type PersistQueryClientProviderProps = QueryClientProviderProps & {
|
|
9
|
+
persistOptions: Omit<PersistQueryClientOptions, 'queryClient'>
|
|
10
|
+
onSuccess?: () => void
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
export const PersistQueryClientProvider = (
|
|
14
|
+
props: PersistQueryClientProviderProps,
|
|
15
|
+
): JSX.Element => {
|
|
16
|
+
const [isRestoring, setIsRestoring] = createSignal(true)
|
|
17
|
+
|
|
18
|
+
let unsub: undefined | (() => void)
|
|
19
|
+
createComputed<() => void>((cleanup) => {
|
|
20
|
+
cleanup?.()
|
|
21
|
+
let isStale = false
|
|
22
|
+
setIsRestoring(true)
|
|
23
|
+
const [unsubscribe, promise] = persistQueryClient({
|
|
24
|
+
...props.persistOptions,
|
|
25
|
+
queryClient: props.client,
|
|
26
|
+
})
|
|
27
|
+
|
|
28
|
+
promise.then(async () => {
|
|
29
|
+
if (isStale) return
|
|
30
|
+
try {
|
|
31
|
+
await props.onSuccess?.()
|
|
32
|
+
} finally {
|
|
33
|
+
setIsRestoring(false)
|
|
34
|
+
}
|
|
35
|
+
})
|
|
36
|
+
|
|
37
|
+
unsub = () => {
|
|
38
|
+
isStale = true
|
|
39
|
+
unsubscribe()
|
|
40
|
+
}
|
|
41
|
+
return unsub
|
|
42
|
+
})
|
|
43
|
+
|
|
44
|
+
onCleanup(() => unsub?.())
|
|
45
|
+
return (
|
|
46
|
+
<QueryClientProvider client={props.client}>
|
|
47
|
+
<IsRestoringProvider value={isRestoring}>
|
|
48
|
+
{props.children}
|
|
49
|
+
</IsRestoringProvider>
|
|
50
|
+
</QueryClientProvider>
|
|
51
|
+
)
|
|
52
|
+
}
|
|
@@ -0,0 +1,582 @@
|
|
|
1
|
+
import '@testing-library/jest-dom'
|
|
2
|
+
import { render, screen, waitFor } from '@solidjs/testing-library'
|
|
3
|
+
import { QueryClient, createQueries, createQuery } from '@tanstack/solid-query'
|
|
4
|
+
import { persistQueryClientSave } from '@tanstack/query-persist-client-core'
|
|
5
|
+
import { createEffect, createSignal, onMount } from 'solid-js'
|
|
6
|
+
|
|
7
|
+
import { PersistQueryClientProvider } from '../PersistQueryClientProvider'
|
|
8
|
+
import { createQueryClient, queryKey, sleep } from './utils'
|
|
9
|
+
import type {
|
|
10
|
+
PersistedClient,
|
|
11
|
+
Persister,
|
|
12
|
+
} from '@tanstack/query-persist-client-core'
|
|
13
|
+
|
|
14
|
+
const createMockPersister = (): Persister => {
|
|
15
|
+
let storedState: PersistedClient | undefined
|
|
16
|
+
|
|
17
|
+
return {
|
|
18
|
+
async persistClient(persistClient: PersistedClient) {
|
|
19
|
+
storedState = persistClient
|
|
20
|
+
},
|
|
21
|
+
async restoreClient() {
|
|
22
|
+
await sleep(10)
|
|
23
|
+
return storedState
|
|
24
|
+
},
|
|
25
|
+
removeClient() {
|
|
26
|
+
storedState = undefined
|
|
27
|
+
},
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const createMockErrorPersister = (
|
|
32
|
+
removeClient: Persister['removeClient'],
|
|
33
|
+
): [Error, Persister] => {
|
|
34
|
+
const error = new Error('restore failed')
|
|
35
|
+
return [
|
|
36
|
+
error,
|
|
37
|
+
{
|
|
38
|
+
async persistClient() {
|
|
39
|
+
// noop
|
|
40
|
+
},
|
|
41
|
+
async restoreClient() {
|
|
42
|
+
await sleep(10)
|
|
43
|
+
throw error
|
|
44
|
+
},
|
|
45
|
+
removeClient,
|
|
46
|
+
},
|
|
47
|
+
]
|
|
48
|
+
}
|
|
49
|
+
|
|
50
|
+
describe('PersistQueryClientProvider', () => {
|
|
51
|
+
test('restores cache from persister', async () => {
|
|
52
|
+
const key = queryKey()
|
|
53
|
+
const states: Array<{
|
|
54
|
+
status: string
|
|
55
|
+
fetchStatus: string
|
|
56
|
+
data: string | undefined
|
|
57
|
+
}> = []
|
|
58
|
+
|
|
59
|
+
const queryClient = createQueryClient()
|
|
60
|
+
await queryClient.prefetchQuery({
|
|
61
|
+
queryKey: key,
|
|
62
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
63
|
+
})
|
|
64
|
+
|
|
65
|
+
const persister = createMockPersister()
|
|
66
|
+
|
|
67
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
68
|
+
|
|
69
|
+
queryClient.clear()
|
|
70
|
+
|
|
71
|
+
function Page() {
|
|
72
|
+
const state = createQuery(() => ({
|
|
73
|
+
queryKey: key,
|
|
74
|
+
queryFn: async () => {
|
|
75
|
+
await sleep(10)
|
|
76
|
+
return 'fetched'
|
|
77
|
+
},
|
|
78
|
+
}))
|
|
79
|
+
createEffect(() =>
|
|
80
|
+
states.push({
|
|
81
|
+
status: state.status,
|
|
82
|
+
fetchStatus: state.fetchStatus,
|
|
83
|
+
data: state.data,
|
|
84
|
+
}),
|
|
85
|
+
)
|
|
86
|
+
|
|
87
|
+
return (
|
|
88
|
+
<div>
|
|
89
|
+
<h1>{state.data}</h1>
|
|
90
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
91
|
+
</div>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
render(() => (
|
|
96
|
+
<PersistQueryClientProvider
|
|
97
|
+
client={queryClient}
|
|
98
|
+
persistOptions={{ persister }}
|
|
99
|
+
>
|
|
100
|
+
<Page />
|
|
101
|
+
</PersistQueryClientProvider>
|
|
102
|
+
))
|
|
103
|
+
|
|
104
|
+
await waitFor(() => screen.getByText('fetchStatus: idle'))
|
|
105
|
+
await waitFor(() => screen.getByText('hydrated'))
|
|
106
|
+
await waitFor(() => screen.getByText('fetched'))
|
|
107
|
+
|
|
108
|
+
expect(states).toHaveLength(3)
|
|
109
|
+
|
|
110
|
+
expect(states[0]).toMatchObject({
|
|
111
|
+
status: 'pending',
|
|
112
|
+
fetchStatus: 'idle',
|
|
113
|
+
data: undefined,
|
|
114
|
+
})
|
|
115
|
+
|
|
116
|
+
expect(states[1]).toMatchObject({
|
|
117
|
+
status: 'success',
|
|
118
|
+
fetchStatus: 'fetching',
|
|
119
|
+
data: 'hydrated',
|
|
120
|
+
})
|
|
121
|
+
|
|
122
|
+
expect(states[2]).toMatchObject({
|
|
123
|
+
status: 'success',
|
|
124
|
+
fetchStatus: 'idle',
|
|
125
|
+
data: 'fetched',
|
|
126
|
+
})
|
|
127
|
+
})
|
|
128
|
+
|
|
129
|
+
test('should also put useQueries into idle state', async () => {
|
|
130
|
+
const key = queryKey()
|
|
131
|
+
const states: Array<{
|
|
132
|
+
status: string
|
|
133
|
+
fetchStatus: string
|
|
134
|
+
data: string | undefined
|
|
135
|
+
}> = []
|
|
136
|
+
|
|
137
|
+
const queryClient = createQueryClient()
|
|
138
|
+
await queryClient.prefetchQuery({
|
|
139
|
+
queryKey: key,
|
|
140
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
141
|
+
})
|
|
142
|
+
|
|
143
|
+
const persister = createMockPersister()
|
|
144
|
+
|
|
145
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
146
|
+
|
|
147
|
+
queryClient.clear()
|
|
148
|
+
|
|
149
|
+
function Page() {
|
|
150
|
+
const [state] = createQueries(() => ({
|
|
151
|
+
queries: [
|
|
152
|
+
{
|
|
153
|
+
queryKey: key,
|
|
154
|
+
queryFn: async (): Promise<string> => {
|
|
155
|
+
await sleep(10)
|
|
156
|
+
return 'fetched'
|
|
157
|
+
},
|
|
158
|
+
},
|
|
159
|
+
] as const,
|
|
160
|
+
}))
|
|
161
|
+
|
|
162
|
+
createEffect(() =>
|
|
163
|
+
states.push({
|
|
164
|
+
status: state.status,
|
|
165
|
+
fetchStatus: state.fetchStatus,
|
|
166
|
+
data: state.data,
|
|
167
|
+
}),
|
|
168
|
+
)
|
|
169
|
+
|
|
170
|
+
return (
|
|
171
|
+
<div>
|
|
172
|
+
<h1>{state.data}</h1>
|
|
173
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
174
|
+
</div>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
render(() => (
|
|
179
|
+
<PersistQueryClientProvider
|
|
180
|
+
client={queryClient}
|
|
181
|
+
persistOptions={{ persister }}
|
|
182
|
+
>
|
|
183
|
+
<Page />
|
|
184
|
+
</PersistQueryClientProvider>
|
|
185
|
+
))
|
|
186
|
+
|
|
187
|
+
await waitFor(() => screen.getByText('fetchStatus: idle'))
|
|
188
|
+
await waitFor(() => screen.getByText('hydrated'))
|
|
189
|
+
await waitFor(() => screen.getByText('fetched'))
|
|
190
|
+
|
|
191
|
+
expect(states).toHaveLength(3)
|
|
192
|
+
|
|
193
|
+
expect(states[0]).toMatchObject({
|
|
194
|
+
status: 'pending',
|
|
195
|
+
fetchStatus: 'idle',
|
|
196
|
+
data: undefined,
|
|
197
|
+
})
|
|
198
|
+
|
|
199
|
+
expect(states[1]).toMatchObject({
|
|
200
|
+
status: 'success',
|
|
201
|
+
fetchStatus: 'fetching',
|
|
202
|
+
data: 'hydrated',
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
expect(states[2]).toMatchObject({
|
|
206
|
+
status: 'success',
|
|
207
|
+
fetchStatus: 'idle',
|
|
208
|
+
data: 'fetched',
|
|
209
|
+
})
|
|
210
|
+
})
|
|
211
|
+
|
|
212
|
+
test('should show initialData while restoring', async () => {
|
|
213
|
+
const key = queryKey()
|
|
214
|
+
const states: Array<{
|
|
215
|
+
status: string
|
|
216
|
+
fetchStatus: string
|
|
217
|
+
data: string | undefined
|
|
218
|
+
}> = []
|
|
219
|
+
|
|
220
|
+
const queryClient = createQueryClient()
|
|
221
|
+
await queryClient.prefetchQuery({
|
|
222
|
+
queryKey: key,
|
|
223
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
const persister = createMockPersister()
|
|
227
|
+
|
|
228
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
229
|
+
|
|
230
|
+
queryClient.clear()
|
|
231
|
+
|
|
232
|
+
function Page() {
|
|
233
|
+
const state = createQuery(() => ({
|
|
234
|
+
queryKey: key,
|
|
235
|
+
queryFn: async () => {
|
|
236
|
+
await sleep(10)
|
|
237
|
+
return 'fetched'
|
|
238
|
+
},
|
|
239
|
+
initialData: 'initial',
|
|
240
|
+
// make sure that initial data is older than the hydration data
|
|
241
|
+
// otherwise initialData would be newer and takes precedence
|
|
242
|
+
initialDataUpdatedAt: 1,
|
|
243
|
+
}))
|
|
244
|
+
|
|
245
|
+
createEffect(() =>
|
|
246
|
+
states.push({
|
|
247
|
+
status: state.status,
|
|
248
|
+
fetchStatus: state.fetchStatus,
|
|
249
|
+
data: state.data,
|
|
250
|
+
}),
|
|
251
|
+
)
|
|
252
|
+
|
|
253
|
+
return (
|
|
254
|
+
<div>
|
|
255
|
+
<h1>{state.data}</h1>
|
|
256
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
257
|
+
</div>
|
|
258
|
+
)
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
render(() => (
|
|
262
|
+
<PersistQueryClientProvider
|
|
263
|
+
client={queryClient}
|
|
264
|
+
persistOptions={{ persister }}
|
|
265
|
+
>
|
|
266
|
+
<Page />
|
|
267
|
+
</PersistQueryClientProvider>
|
|
268
|
+
))
|
|
269
|
+
|
|
270
|
+
await waitFor(() => screen.getByText('initial'))
|
|
271
|
+
await waitFor(() => screen.getByText('hydrated'))
|
|
272
|
+
await waitFor(() => screen.getByText('fetched'))
|
|
273
|
+
|
|
274
|
+
expect(states).toHaveLength(3)
|
|
275
|
+
|
|
276
|
+
expect(states[0]).toMatchObject({
|
|
277
|
+
status: 'success',
|
|
278
|
+
fetchStatus: 'idle',
|
|
279
|
+
data: 'initial',
|
|
280
|
+
})
|
|
281
|
+
|
|
282
|
+
expect(states[1]).toMatchObject({
|
|
283
|
+
status: 'success',
|
|
284
|
+
fetchStatus: 'fetching',
|
|
285
|
+
data: 'hydrated',
|
|
286
|
+
})
|
|
287
|
+
|
|
288
|
+
expect(states[2]).toMatchObject({
|
|
289
|
+
status: 'success',
|
|
290
|
+
fetchStatus: 'idle',
|
|
291
|
+
data: 'fetched',
|
|
292
|
+
})
|
|
293
|
+
})
|
|
294
|
+
|
|
295
|
+
test('should not refetch after restoring when data is fresh', async () => {
|
|
296
|
+
const key = queryKey()
|
|
297
|
+
const states: Array<{
|
|
298
|
+
status: string
|
|
299
|
+
fetchStatus: string
|
|
300
|
+
data: string | undefined
|
|
301
|
+
}> = []
|
|
302
|
+
|
|
303
|
+
const queryClient = createQueryClient()
|
|
304
|
+
await queryClient.prefetchQuery({
|
|
305
|
+
queryKey: key,
|
|
306
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
307
|
+
})
|
|
308
|
+
|
|
309
|
+
const persister = createMockPersister()
|
|
310
|
+
|
|
311
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
312
|
+
|
|
313
|
+
queryClient.clear()
|
|
314
|
+
|
|
315
|
+
let fetched = false
|
|
316
|
+
|
|
317
|
+
function Page() {
|
|
318
|
+
const state = createQuery(() => ({
|
|
319
|
+
queryKey: key,
|
|
320
|
+
queryFn: async () => {
|
|
321
|
+
fetched = true
|
|
322
|
+
await sleep(10)
|
|
323
|
+
return 'fetched'
|
|
324
|
+
},
|
|
325
|
+
staleTime: Infinity,
|
|
326
|
+
}))
|
|
327
|
+
|
|
328
|
+
createEffect(
|
|
329
|
+
() =>
|
|
330
|
+
states.push({
|
|
331
|
+
status: state.status,
|
|
332
|
+
fetchStatus: state.fetchStatus,
|
|
333
|
+
data: state.data,
|
|
334
|
+
}),
|
|
335
|
+
console.log(state.data),
|
|
336
|
+
)
|
|
337
|
+
|
|
338
|
+
return (
|
|
339
|
+
<div>
|
|
340
|
+
<h1>data: {state.data ?? 'null'}</h1>
|
|
341
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
342
|
+
</div>
|
|
343
|
+
)
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
render(() => (
|
|
347
|
+
<PersistQueryClientProvider
|
|
348
|
+
client={queryClient}
|
|
349
|
+
persistOptions={{ persister }}
|
|
350
|
+
>
|
|
351
|
+
<Page />
|
|
352
|
+
</PersistQueryClientProvider>
|
|
353
|
+
))
|
|
354
|
+
|
|
355
|
+
await waitFor(() => screen.getByText('data: null'))
|
|
356
|
+
await waitFor(() => screen.getByText('data: hydrated'))
|
|
357
|
+
|
|
358
|
+
expect(states).toHaveLength(2)
|
|
359
|
+
|
|
360
|
+
expect(fetched).toBe(false)
|
|
361
|
+
|
|
362
|
+
expect(states[0]).toMatchObject({
|
|
363
|
+
status: 'pending',
|
|
364
|
+
fetchStatus: 'idle',
|
|
365
|
+
data: undefined,
|
|
366
|
+
})
|
|
367
|
+
|
|
368
|
+
expect(states[1]).toMatchObject({
|
|
369
|
+
status: 'success',
|
|
370
|
+
fetchStatus: 'idle',
|
|
371
|
+
data: 'hydrated',
|
|
372
|
+
})
|
|
373
|
+
})
|
|
374
|
+
|
|
375
|
+
test('should call onSuccess after successful restoring', async () => {
|
|
376
|
+
const key = queryKey()
|
|
377
|
+
|
|
378
|
+
const queryClient = createQueryClient()
|
|
379
|
+
await queryClient.prefetchQuery({
|
|
380
|
+
queryKey: key,
|
|
381
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
382
|
+
})
|
|
383
|
+
|
|
384
|
+
const persister = createMockPersister()
|
|
385
|
+
|
|
386
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
387
|
+
|
|
388
|
+
queryClient.clear()
|
|
389
|
+
|
|
390
|
+
function Page() {
|
|
391
|
+
const state = createQuery(() => ({
|
|
392
|
+
queryKey: key,
|
|
393
|
+
queryFn: async () => {
|
|
394
|
+
await sleep(10)
|
|
395
|
+
return 'fetched'
|
|
396
|
+
},
|
|
397
|
+
}))
|
|
398
|
+
|
|
399
|
+
return (
|
|
400
|
+
<div>
|
|
401
|
+
<h1>{state.data}</h1>
|
|
402
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
403
|
+
</div>
|
|
404
|
+
)
|
|
405
|
+
}
|
|
406
|
+
|
|
407
|
+
const onSuccess = vi.fn()
|
|
408
|
+
|
|
409
|
+
render(() => (
|
|
410
|
+
<PersistQueryClientProvider
|
|
411
|
+
client={queryClient}
|
|
412
|
+
persistOptions={{ persister }}
|
|
413
|
+
onSuccess={onSuccess}
|
|
414
|
+
>
|
|
415
|
+
<Page />
|
|
416
|
+
</PersistQueryClientProvider>
|
|
417
|
+
))
|
|
418
|
+
expect(onSuccess).toHaveBeenCalledTimes(0)
|
|
419
|
+
|
|
420
|
+
await waitFor(() => screen.getByText('hydrated'))
|
|
421
|
+
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
422
|
+
await waitFor(() => screen.getByText('fetched'))
|
|
423
|
+
})
|
|
424
|
+
|
|
425
|
+
test('should remove cache after non-successful restoring', async () => {
|
|
426
|
+
const key = queryKey()
|
|
427
|
+
|
|
428
|
+
const onErrorMock = vi
|
|
429
|
+
.spyOn(console, 'error')
|
|
430
|
+
.mockImplementation(() => undefined)
|
|
431
|
+
const queryClient = createQueryClient()
|
|
432
|
+
const removeClient = vi.fn()
|
|
433
|
+
|
|
434
|
+
const [error, persister] = createMockErrorPersister(removeClient)
|
|
435
|
+
|
|
436
|
+
function Page() {
|
|
437
|
+
const state = createQuery(() => ({
|
|
438
|
+
queryKey: key,
|
|
439
|
+
queryFn: async () => {
|
|
440
|
+
await sleep(10)
|
|
441
|
+
return 'fetched'
|
|
442
|
+
},
|
|
443
|
+
}))
|
|
444
|
+
|
|
445
|
+
return (
|
|
446
|
+
<div>
|
|
447
|
+
<h1>{state.data}</h1>
|
|
448
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
449
|
+
</div>
|
|
450
|
+
)
|
|
451
|
+
}
|
|
452
|
+
|
|
453
|
+
render(() => (
|
|
454
|
+
<PersistQueryClientProvider
|
|
455
|
+
client={queryClient}
|
|
456
|
+
persistOptions={{ persister }}
|
|
457
|
+
>
|
|
458
|
+
<Page />
|
|
459
|
+
</PersistQueryClientProvider>
|
|
460
|
+
))
|
|
461
|
+
|
|
462
|
+
await waitFor(() => screen.getByText('fetched'))
|
|
463
|
+
expect(removeClient).toHaveBeenCalledTimes(1)
|
|
464
|
+
expect(onErrorMock).toHaveBeenCalledTimes(1)
|
|
465
|
+
expect(onErrorMock).toHaveBeenNthCalledWith(1, error)
|
|
466
|
+
onErrorMock.mockRestore()
|
|
467
|
+
})
|
|
468
|
+
|
|
469
|
+
test('should be able to persist into multiple clients', async () => {
|
|
470
|
+
const key = queryKey()
|
|
471
|
+
const states: Array<{
|
|
472
|
+
status: string
|
|
473
|
+
fetchStatus: string
|
|
474
|
+
data: string | undefined
|
|
475
|
+
}> = []
|
|
476
|
+
|
|
477
|
+
const queryClient = createQueryClient()
|
|
478
|
+
await queryClient.prefetchQuery({
|
|
479
|
+
queryKey: key,
|
|
480
|
+
queryFn: () => Promise.resolve('hydrated'),
|
|
481
|
+
})
|
|
482
|
+
|
|
483
|
+
const persister = createMockPersister()
|
|
484
|
+
|
|
485
|
+
await persistQueryClientSave({ queryClient, persister })
|
|
486
|
+
|
|
487
|
+
queryClient.clear()
|
|
488
|
+
|
|
489
|
+
const onSuccess = vi.fn()
|
|
490
|
+
|
|
491
|
+
const queryFn1 = vi.fn().mockImplementation(async () => {
|
|
492
|
+
await sleep(10)
|
|
493
|
+
return 'queryFn1'
|
|
494
|
+
})
|
|
495
|
+
const queryFn2 = vi.fn().mockImplementation(async () => {
|
|
496
|
+
await sleep(10)
|
|
497
|
+
return 'queryFn2'
|
|
498
|
+
})
|
|
499
|
+
|
|
500
|
+
function App() {
|
|
501
|
+
const [client, setClient] = createSignal(
|
|
502
|
+
new QueryClient({
|
|
503
|
+
defaultOptions: {
|
|
504
|
+
queries: {
|
|
505
|
+
queryFn: queryFn1,
|
|
506
|
+
},
|
|
507
|
+
},
|
|
508
|
+
}),
|
|
509
|
+
)
|
|
510
|
+
|
|
511
|
+
onMount(() => {
|
|
512
|
+
setClient(
|
|
513
|
+
new QueryClient({
|
|
514
|
+
defaultOptions: {
|
|
515
|
+
queries: {
|
|
516
|
+
queryFn: queryFn2,
|
|
517
|
+
},
|
|
518
|
+
},
|
|
519
|
+
}),
|
|
520
|
+
)
|
|
521
|
+
})
|
|
522
|
+
|
|
523
|
+
return (
|
|
524
|
+
<PersistQueryClientProvider
|
|
525
|
+
client={client()}
|
|
526
|
+
persistOptions={{ persister }}
|
|
527
|
+
onSuccess={onSuccess}
|
|
528
|
+
>
|
|
529
|
+
<Page />
|
|
530
|
+
</PersistQueryClientProvider>
|
|
531
|
+
)
|
|
532
|
+
}
|
|
533
|
+
|
|
534
|
+
function Page() {
|
|
535
|
+
const state = createQuery(() => ({ queryKey: key }))
|
|
536
|
+
|
|
537
|
+
createEffect(() =>
|
|
538
|
+
states.push({
|
|
539
|
+
status: state.status,
|
|
540
|
+
fetchStatus: state.fetchStatus,
|
|
541
|
+
data: state.data as string | undefined,
|
|
542
|
+
}),
|
|
543
|
+
)
|
|
544
|
+
|
|
545
|
+
return (
|
|
546
|
+
<div>
|
|
547
|
+
<h1>{String(state.data)}</h1>
|
|
548
|
+
<h2>fetchStatus: {state.fetchStatus}</h2>
|
|
549
|
+
</div>
|
|
550
|
+
)
|
|
551
|
+
}
|
|
552
|
+
|
|
553
|
+
render(() => <App />)
|
|
554
|
+
|
|
555
|
+
await waitFor(() => screen.getByText('hydrated'))
|
|
556
|
+
await waitFor(() => screen.getByText('queryFn2'))
|
|
557
|
+
|
|
558
|
+
expect(queryFn1).toHaveBeenCalledTimes(0)
|
|
559
|
+
expect(queryFn2).toHaveBeenCalledTimes(1)
|
|
560
|
+
expect(onSuccess).toHaveBeenCalledTimes(1)
|
|
561
|
+
|
|
562
|
+
expect(states).toHaveLength(3)
|
|
563
|
+
|
|
564
|
+
expect(states[0]).toMatchObject({
|
|
565
|
+
status: 'pending',
|
|
566
|
+
fetchStatus: 'idle',
|
|
567
|
+
data: undefined,
|
|
568
|
+
})
|
|
569
|
+
|
|
570
|
+
expect(states[1]).toMatchObject({
|
|
571
|
+
status: 'success',
|
|
572
|
+
fetchStatus: 'fetching',
|
|
573
|
+
data: 'hydrated',
|
|
574
|
+
})
|
|
575
|
+
|
|
576
|
+
expect(states[2]).toMatchObject({
|
|
577
|
+
status: 'success',
|
|
578
|
+
fetchStatus: 'idle',
|
|
579
|
+
data: 'queryFn2',
|
|
580
|
+
})
|
|
581
|
+
})
|
|
582
|
+
})
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { QueryClient } from '@tanstack/solid-query'
|
|
2
|
+
import type { QueryClientConfig } from '@tanstack/solid-query'
|
|
3
|
+
|
|
4
|
+
export function createQueryClient(config?: QueryClientConfig): QueryClient {
|
|
5
|
+
return new QueryClient(config)
|
|
6
|
+
}
|
|
7
|
+
|
|
8
|
+
let queryKeyCount = 0
|
|
9
|
+
export function queryKey(): Array<string> {
|
|
10
|
+
queryKeyCount++
|
|
11
|
+
return [`query_${queryKeyCount}`]
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export function sleep(timeout: number): Promise<void> {
|
|
15
|
+
return new Promise((resolve, _reject) => {
|
|
16
|
+
setTimeout(resolve, timeout)
|
|
17
|
+
})
|
|
18
|
+
}
|
package/src/index.ts
ADDED