Skip to content

feat(persistQueryClient): PersistQueryClientProvider #3248

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 34 commits into from
Mar 24, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
34 commits
Select commit Hold shift + click to select a range
5a0aab7
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Jan 30, 2022
5c4de91
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 6, 2022
985c564
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 6, 2022
00d0ad4
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 6, 2022
261777c
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 7, 2022
0f6de7a
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 10, 2022
2d410fb
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 12, 2022
363e3f7
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
459ac56
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
b301ce2
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
82ed46d
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 12, 2022
c86607f
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
da52e82
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
00a6bcd
feat(persistQueryClient): PersistQueryClientProvider
TkDodo Feb 12, 2022
90ce9ff
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 26, 2022
2c21e75
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 26, 2022
74f9748
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Feb 26, 2022
6bef21c
Merge branch 'alpha' into feature/persistQueryClient
TkDodo Feb 26, 2022
fc00442
make restore in mockPersister a bit slower to stabilize tests
TkDodo Feb 26, 2022
7befff0
Merge branch 'alpha' into feature/persistQueryClient
TkDodo Feb 26, 2022
9b05e02
Merge branch 'alpha' into feature/persistQueryClient
TkDodo Feb 27, 2022
879495a
better persistQueryClient docs
TkDodo Feb 27, 2022
c6565db
Merge branch 'alpha' into feature/persistQueryClient
TkDodo Feb 28, 2022
507edca
feat(PersistQueryClientProvider): make sure we can hydrate into multi…
TkDodo Feb 28, 2022
8f9c746
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Mar 3, 2022
702159f
offline example
TkDodo Mar 13, 2022
4146a69
extract to custom hook
TkDodo Mar 13, 2022
291b0a5
Merge remote-tracking branch 'react-query/alpha' into feature/persist…
TkDodo Mar 13, 2022
e78a470
remove onError callback
TkDodo Mar 13, 2022
91e2afb
just ignore stale hydrations if the client changes
TkDodo Mar 19, 2022
29d52f8
Revert "just ignore stale hydrations if the client changes"
TkDodo Mar 19, 2022
433fd8a
just ignore stale hydrations if the client changes
TkDodo Mar 19, 2022
2570c8a
Merge remote-tracking branch 'tannerlinsley/alpha' into feature/persi…
TkDodo Mar 24, 2022
b2cd6b0
since QueryClientProviderProps is now a union type, we can't extend i…
TkDodo Mar 24, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions docs/src/manifests/manifest.json
Original file line number Diff line number Diff line change
Expand Up @@ -326,6 +326,11 @@
"title": "React Native",
"path": "/examples/react-native",
"editUrl": "/examples/react-native.mdx"
},
{
"title": "Offline Queries and Mutations",
"path": "/examples/offline",
"editUrl": "/examples/offline.mdx"
}
]
},
Expand Down
23 changes: 23 additions & 0 deletions docs/src/pages/examples/offline.mdx
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
---
id: offline
title: Offline Queries and Mutations
toc: false
---

- [Open in CodeSandbox](https://codesandbox.io/s/github/tannerlinsley/react-query/tree/alpha/examples/offline)
- [View Source](https://github.com/tannerlinsley/react-query/tree/alpha/examples/offline)

<iframe
src="https://codesandbox.io/embed/github/tannerlinsley/react-query/tree/alpha/examples/offline?autoresize=1&fontsize=14&theme=dark"
title="tannerlinsley/react-query: offline"
sandbox="allow-forms allow-modals allow-popups allow-presentation allow-same-origin allow-scripts"
style={{
width: '100%',
height: '80vh',
border: '0',
borderRadius: 8,
overflow: 'hidden',
position: 'static',
zIndex: 0,
}}
></iframe>
62 changes: 62 additions & 0 deletions docs/src/pages/plugins/persistQueryClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -159,6 +159,68 @@ There are actually three interfaces available:
- `PersistedQueryClientRestoreOptions` is used for `persistQueryClientRestore` (doesn't use `dehydrateOptions`).
- `PersistQueryClientOptions` is used for `persistQueryClient`

## Usage with React

[persistQueryClient](#persistQueryClient) will try to restore the cache and automatically subscribes to further changes, thus syncing your client to the provided storage.

However, restoring is asynchronous, because all persisters are async by nature, which means that if you render your App while you are restoring, you might get into race conditions if a query mounts and fetches at the same time.

Further, if you subscribe to changes outside of the React component lifecycle, you have no way of unsubscribing:

```js
// 🚨 never unsubscribes from syncing
persistQueryClient({
queryClient,
persister: localStoragePersister,
})

// 🚨 happens at the same time as restoring
ReactDOM.render(<App />, rootElement)
```

### PeristQueryClientProvider

For this use-case, you can use the `PersistQueryClientProvider`. It will make sure to subscribe / unsubscribe correctly according to the React component lifecycle, and it will also make sure that queries will not start fetching while we are still restoring. Queries will still render though, they will just be put into `fetchingState: 'idle'` until data has been restored. Then, they will refetch unless the restored data is _fresh_ enough, and _initialData_ will also be respected. It can be used _instead of_ the normal [QueryClientProvider](../reference/QueryClientProvider):

```jsx

import { PersistQueryClientProvider } from 'react-query/persistQueryClient'
import { createWebStoragePersister } from 'react-query/createWebStoragePersister'

const queryClient = new QueryClient({
defaultOptions: {
queries: {
cacheTime: 1000 * 60 * 60 * 24, // 24 hours
},
},
})

const persister = createWebStoragePersister({
storage: window.localStorage,
})

ReactDOM.render(
<PersistQueryClientProvider
client={queryClient}
persistOptions={{ persister }}
>
<App />
</PersistQueryClientProvider>,
rootElement
)
```

#### Props

`PersistQueryClientProvider` takes the same props as [QueryClientProvider](../reference/QueryClientProvider), and additionally:

- `persistOptions: PersistQueryClientOptions`
- all [options](#options) you cann pass to [persistQueryClient](#persistqueryclient) minus the QueryClient itself
- `onSuccess?: () => void`
- optional
- will be called when the initial restore is finished
- can be used to [resumePausedMutations](../reference/QueryClient#queryclientresumepausedmutations)

## Persisters

### Persisters Interface
Expand Down
9 changes: 9 additions & 0 deletions docs/src/pages/reference/QueryClient.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ Its available methods are:
- [`queryClient.getQueryCache`](#queryclientgetquerycache)
- [`queryClient.getMutationCache`](#queryclientgetmutationcache)
- [`queryClient.clear`](#queryclientclear)
- - [`queryClient.resumePausedMutations`](#queryclientresumepausedmutations)

**Options**

Expand Down Expand Up @@ -563,3 +564,11 @@ The `clear` method clears all connected caches.
```js
queryClient.clear()
```

## `queryClient.resumePausedMutations`

Can be used to resume mutations that have been paused because there was no network connection.

```js
queryClient.resumePausedMutations()
```
3 changes: 3 additions & 0 deletions examples/offline/.babelrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
{
"presets": ["react-app"]
}
7 changes: 7 additions & 0 deletions examples/offline/.eslintrc
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
{
"extends": ["react-app", "prettier"],
"rules": {
// "eqeqeq": 0,
// "jsx-a11y/anchor-is-valid": 0
}
}
26 changes: 26 additions & 0 deletions examples/offline/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# See https://help.github.com/articles/ignoring-files/ for more about ignoring files.

# dependencies
/node_modules
/.pnp
.pnp.js

# testing
/coverage

# production
/build

yarn.lock
package-lock.json

# misc
.DS_Store
.env.local
.env.development.local
.env.test.local
.env.production.local

npm-debug.log*
yarn-debug.log*
yarn-error.log*
1 change: 1 addition & 0 deletions examples/offline/.prettierrc
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
{}
37 changes: 37 additions & 0 deletions examples/offline/.rescriptsrc.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
const path = require("path");
const resolveFrom = require("resolve-from");

const fixLinkedDependencies = (config) => {
config.resolve = {
...config.resolve,
alias: {
...config.resolve.alias,
react$: resolveFrom(path.resolve("node_modules"), "react"),
"react-dom$": resolveFrom(path.resolve("node_modules"), "react-dom"),
},
};
return config;
};

const includeSrcDirectory = (config) => {
config.resolve = {
...config.resolve,
modules: [path.resolve("src"), ...config.resolve.modules],
};
return config;
};

const allowOutsideSrc = (config) => {
config.resolve.plugins = config.resolve.plugins.filter(
(p) => p.constructor.name !== "ModuleScopePlugin"
);
return config;
};

module.exports = [
["use-babel-config", ".babelrc"],
["use-eslint-config", ".eslintrc"],
fixLinkedDependencies,
allowOutsideSrc,
// includeSrcDirectory,
];
6 changes: 6 additions & 0 deletions examples/offline/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# Example

To run this example:

- `npm install` or `yarn`
- `npm run start` or `yarn start`
36 changes: 36 additions & 0 deletions examples/offline/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
{
"private": true,
"scripts": {
"start": "rescripts start",
"build": "rescripts build",
"test": "rescripts test",
"eject": "rescripts eject"
},
"dependencies": {
"@tanstack/react-location": "^3.7.0",
"ky": "^0.30.0",
"react": "^17.0.2",
"react-dom": "^17.0.2",
"react-hot-toast": "^2.2.0",
"react-query": "^4.0.0-alpha.19",
"react-scripts": "3.0.1"
},
"devDependencies": {
"@rescripts/cli": "^0.0.11",
"@rescripts/rescript-use-babel-config": "^0.0.8",
"@rescripts/rescript-use-eslint-config": "^0.0.9",
"babel-eslint": "10.0.1"
},
"browserslist": {
"production": [
">0.2%",
"not dead",
"not op_mini all"
],
"development": [
"last 1 chrome version",
"last 1 firefox version",
"last 1 safari version"
]
}
}
1 change: 1 addition & 0 deletions examples/offline/public/favicon.ico
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
https://rawcdn.githack.com/tannerlinsley/react-query/master/examples/simple/public/favicon.ico
38 changes: 38 additions & 0 deletions examples/offline/public/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="shortcut icon" href="%PUBLIC_URL%/favicon.ico" />
<meta name="viewport" content="width=device-width, initial-scale=1" />
<meta name="theme-color" content="#000000" />
<!--
manifest.json provides metadata used when your web app is installed on a
user's mobile device or desktop. See https://developers.google.com/web/fundamentals/web-app-manifest/
-->
<link rel="manifest" href="%PUBLIC_URL%/manifest.json" />
<!--
Notice the use of %PUBLIC_URL% in the tags above.
It will be replaced with the URL of the `public` folder during the build.
Only files inside the `public` folder can be referenced from the HTML.

Unlike "/favicon.ico" or "favicon.ico", "%PUBLIC_URL%/favicon.ico" will
work correctly both with client-side routing and a non-root public URL.
Learn how to configure a non-root public URL by running `npm run build`.
-->
<title>React App</title>
</head>
<body>
<noscript>You need to enable JavaScript to run this app.</noscript>
<div id="root"></div>
<!--
This HTML file is a template.
If you open it directly in the browser, you will see an empty page.

You can add webfonts, meta tags, or analytics to this file.
The build step will place the bundled scripts into the <body> tag.

To begin the development, run `npm start` or `yarn start`.
To create a production bundle, use `npm run build` or `yarn build`.
-->
</body>
</html>
15 changes: 15 additions & 0 deletions examples/offline/public/manifest.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"short_name": "React App",
"name": "Create React App Sample",
"icons": [
{
"src": "favicon.ico",
"sizes": "64x64 32x32 24x24 16x16",
"type": "image/x-icon"
}
],
"start_url": ".",
"display": "standalone",
"theme_color": "#000000",
"background_color": "#ffffff"
}
Loading