This is a continuation of things described in issue #461. It's a breakout from the closed PR #570 and builds on the merged PRs #584 and #917.
This PR adds a couple of new public APIs:
dehydrate(queryCache, dehydrateConfig)
hydrate(queryCache, dehydratedQueries)
- A new
hydration/ReactQueryCacheProvider
-component with the additional dehydratedState
-prop
useHydrate(dehydratedQueries)
- A hook that does hydrate
for you in a React-compatible way
Together, this provides a way to dehydrate a cache, pass its serialized form over the wire or persist it somehow, and later hydrate those queries back into an active cache. Main goals are to improve support for server rendering and help with things like persisting to localstorage or other storage.
An important feature of these new APIs is that the shape of the dehydrated cache is meant to be a private implementation detail that consumers should not rely on, which needs to be emphasized in the docs. This means that the de/rehydrate functionality can more easily be built upon in the future without breaking changes.
SSR Example with Next.js
❗ This example previously looked different, but has been updated for useHydrate
A minimal example:
// _app.jsx
import { ReactQueryCacheProvider } from 'react-query/hydration'
export default function MyApp({ Component, pageProps }) {
return (
<ReactQueryCacheProvider initialQueries={pageProps.initialQueries}>
<Component {...pageProps} />
</ReactQueryCacheProvider>
)
}
// pages/posts.jsx
import { makeQueryCache } from 'react-query'
import { dehydrate } from 'react-query/hydration'
export async function getStaticProps() {
const queryCache = makeQueryCache()
await queryCache.prefetchQuery('posts', getPosts)
return {
props: {
initialQueries: dehydrate(queryCache)
}
}
}
function Posts() {
// This useQuery could just as well happen in some deeper child to
// the "Posts"-page, data will be available immediately either way
const { data } = useQuery('posts', getPosts)
// This query was not prefetched on the server and will not start
// fetching until on the client, both patterns are fine to mix
const { data: otherData } = useQuery('posts-2', getPosts)
// ...
}
Config options
There are a few config options that you can set when dehydrating and hydrating. Most notably is the shouldDehydrate
-function, that can filter the queryCache for queries to dehydrate. Usecase when dehydrating is to filter out only the queries you want to persist to localstorage for example.
With or without shouldDehydrate, only successful queries are dehydrated from the cache
dehydrateConfig
type ShouldDehydrateFunction = <TResult, TError = unknown>(
query: Query<TResult, TError>
) => boolean
interface DehydrateConfig {
shouldDehydrate?: ShouldDehydrateFunction
}
Questions
These are a few questions I'm not totally certain about myself and might be worth an extra look when reviewing.
~~Should hydration be its own entry point?~~
Solved, is now its own entry point
~~Should dehydrateQuery
be included in the official APIs?~~~
Solved, was removed in favour of a shouldDehydrate
-function to keep the API lean. Could be exposed publically later on if there is need for it.
~~How should we handle updatedAt
?~~
Solved, updatedAt is now hydrated and also used for determining how far into the future staleness should be scheduled.
Can naming be improved?
There are a bunch of new public APIs, so let's get the naming juust right!
I'm sure I've missed a bunch of stuff I wanted to highlight and/or ask, but hopefully this is a good start for getting feedback and questions. 😅
released v3