Skip to content
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

feat: remove ReactQueryCacheProvider from hydration #943

Merged
merged 1 commit into from
Aug 31, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
28 changes: 8 additions & 20 deletions docs/src/pages/docs/api.md
Original file line number Diff line number Diff line change
Expand Up @@ -792,13 +792,13 @@ setConsole({

## `hydration/dehydrate`

`dehydrate` creates a frozen representation of a `queryCache` that can later be hydrated with `useHydrate`, `hydrate` or by passing it into `hydration/ReactQueryCacheProvider`. This is useful for passing prefetched queries from server to client or persisting queries to localstorage. It only includes currently successful queries by default.
`dehydrate` creates a frozen representation of a `queryCache` that can later be hydrated with `useHydrate`, `hydrate` or `Hydrate`. This is useful for passing prefetched queries from server to client or persisting queries to localstorage. It only includes currently successful queries by default.

```js
import { dehydrate } from 'react-query/hydration'

const dehydratedState = dehydrate(queryCache, {
shouldDehydrate
shouldDehydrate,
})
```

Expand All @@ -824,7 +824,7 @@ const dehydratedState = dehydrate(queryCache, {
`hydrate` adds a previously dehydrated state into a `queryCache`. If the queries included in dehydration already exist in the cache, `hydrate` does not overwrite them.

```js
import { hydrate } from 'react-query/hydration'
import { hydrate } from 'react-query/hydration'

hydrate(queryCache, dehydratedState)
```
Expand Down Expand Up @@ -854,31 +854,19 @@ useHydrate(dehydratedState)
- **Required**
- The state to hydrate

## `hydration/ReactQueryCacheProvider`
## `hydration/Hydrate`

`hydration/ReactQueryCacheProvider` does the same thing as `ReactQueryCacheProvider` but also supports hydrating an initial state into the cache.
`hydration/Hydrate` does the same thing as `useHydrate` but exposed as a component.

```js
import { ReactQueryCacheProvider } from 'react-query/hydration'
import { Hydrate } from 'react-query/hydration'

function App() {
return (
<ReactQueryCacheProvider
queryCache={queryCache}
dehydratedState={dehydratedState}
hydrationConfig={hydrationConfig}>
...
</ReactQueryCacheProvider>
)
return <Hydrate state={dehydratedState}>...</Hydrate>
}
```

**Options**

- `queryCache: QueryCache`
- In instance of queryCache, you can use the `makeQueryCache` factory to create this.
- If not provided, a new cache will be generated.
- `dehydratedState: DehydratedState`
- `state: DehydratedState`
- The state to hydrate
- `hydrationConfig`
- Same config as for `hydrate`
53 changes: 30 additions & 23 deletions docs/src/pages/docs/guides/ssr.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ This approach works well for applications or user-specific pages that might cont

React Query supports two ways of prefetching data on the server and passing that to the client.

* Prefetch the data yourself and pass it in as `initialData`
* Quick to set up for simple cases
* Has some caveats
* Prefetch the query via React Query and use de/rehydration
* Requires slightly more setup up front
- Prefetch the data yourself and pass it in as `initialData`
- Quick to set up for simple cases
- Has some caveats
- Prefetch the query via React Query and use de/rehydration
- Requires slightly more setup up front

The exact implementation of these mechanisms may vary from platform to platform, but we recommend starting with Next.js which supports [2 forms of pre-rendering](https://nextjs.org/docs/basic-features/data-fetching):

Expand Down Expand Up @@ -45,28 +45,29 @@ function Posts(props) {

The setup is minimal and this can be a perfect solution for some cases, but there are a few tradeoffs compared to the full approach:

* If you are calling `useQuery` in a component deeper down in the tree you need to pass the `initialData` down to that point
* If you are calling `useQuery` with the same query in multiple locations, you need to pass `initialData` to all of them
* There is no way to know at what time the query was fetched on the server, so `updatedAt` and determining if the query needs refetching is based on when the page loaded instead
- If you are calling `useQuery` in a component deeper down in the tree you need to pass the `initialData` down to that point
- If you are calling `useQuery` with the same query in multiple locations, you need to pass `initialData` to all of them
- There is no way to know at what time the query was fetched on the server, so `updatedAt` and determining if the query needs refetching is based on when the page loaded instead

## Prefetch the query via React Query and use de/rehydration

React Query supports prefetching a query on the server and handing off or _dehydrating_ that query to the client. This means the server can prerender markup that is immediately available on page load and as soon as JS is available, React Query can upgrade or _hydrate_ those queries with the full functionality of the library. This includes refetching those queries on the client if they have become stale since the time they were rendered on the server.

### Integrating with Next.js

To support caching queries on the server and set up hydration, you start with wrapping your application with `<ReactQueryCacheProvider>` in `_app.js`.

> Note: You need to import `ReactQueryCacheProvider` from `'react-query/hydration'` for it to support hydration!
To support caching queries on the server and set up hydration, you start with wrapping your application with `<ReactQueryCacheProvider>` and `<Hydrate>` in `_app.js`.

```jsx
// _app.jsx
import { ReactQueryCacheProvider } from 'react-query/hydration'
import { ReactQueryCacheProvider } from 'react-query'
import { Hydrate } from 'react-query/hydration'

export default function MyApp({ Component, pageProps }) {
return (
<ReactQueryCacheProvider dehydratedState={pageProps.dehydratedState}>
<Component {...pageProps} />
<ReactQueryCacheProvider>
<Hydrate state={pageProps.dehydratedState}>
<Component {...pageProps} />
</Hydrate>
</ReactQueryCacheProvider>
)
}
Expand All @@ -86,8 +87,8 @@ export async function getStaticProps() {

return {
props: {
dehydratedState: dehydrate(queryCache)
}
dehydratedState: dehydrate(queryCache),
},
}
}

Expand Down Expand Up @@ -119,7 +120,7 @@ Since there are many different possible setups for SSR, it's hard to give a deta
- Call `prefetchQueryCache.prefetchQuery(...)` to prefetch queries
- Dehydrate by using `const dehydratedState = dehydrate(prefetchQueryCache)`
- Render
- Wrap the app in `<ReactQueryCacheProvider>` from `'react-query/hydration'` and pass in `dehydratedState`
- Wrap the app in `<ReactQueryCacheProvider>` and `<Hydrate>` to create a new cache and hydrate the state
- This makes sure a separate `queryCache` is created specifically for rendering
- **Do not** pass in the `prefetchQueryCache` from the last step, the server and client both needs to render from the dehydrated data to avoid React hydration mismatches. This is because queries with errors are excluded from dehydration by default.
- Serialize and embed `dehydratedState` in the markup
Expand All @@ -129,7 +130,7 @@ Since there are many different possible setups for SSR, it's hard to give a deta

- Parse `dehydratedState` from where you put it in the markup
- Render
- Wrap the app in `<ReactQueryCacheProvider>` from `'react-query/hydration'` and pass in `dehydratedState`
- Wrap the app in `<Hydrate>` from `'react-query/hydration'` and pass in the dehyrated state.

This list aims to be exhaustive, but depending on your current setup, the above steps can take more or less work. Here is a barebones example:

Expand All @@ -140,24 +141,30 @@ await prefetchCache.prefetchQuery('key', fn)
const dehydratedState = dehydrate(prefetchCache)

const html = ReactDOM.renderToString(
<ReactQueryCacheProvider dehydratedState={dehydratedState}>
<App />
<ReactQueryCacheProvider>
<Hydrate state={dehyratedState}>
<App />
</Hydrate>
</ReactQueryCacheProvider>
)
res.send(`
<html>
<body>
<div id="app">${html}</div>
<script>window.__REACT_QUERY_INITIAL_QUERIES__ = ${JSON.stringify(dehydratedState)};</script>
<script>window.__REACT_QUERY_INITIAL_QUERIES__ = ${JSON.stringify(
dehydratedState
)};</script>
</body>
</html>
`)

// Client
const dehydratedState = JSON.parse(window.__REACT_QUERY_INITIAL_QUERIES__)
ReactDOM.hydrate(
<ReactQueryCacheProvider dehydratedState={dehydratedState}>
<App />
<ReactQueryCacheProvider>
<Hydrate state={dehyratedState}>
<App />
</Hydrate>
</ReactQueryCacheProvider>
)
```
Expand Down
4 changes: 2 additions & 2 deletions src/hydration/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
export { dehydrate, hydrate } from './hydration'
export { useHydrate, ReactQueryCacheProvider } from './react'
export { useHydrate, Hydrate } from './react'

// Types
export type {
Expand All @@ -9,4 +9,4 @@ export type {
ShouldDehydrateFunction,
DehydrateConfig,
} from './hydration'
export type { HydrationCacheProviderProps } from './react'
export type { HydrateProps } from './react'
33 changes: 6 additions & 27 deletions src/hydration/react.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import React from 'react'
import {
useQueryCache,
ReactQueryCacheProvider as CacheProvider,
} from 'react-query'
import { hydrate } from './hydration'
import { useQueryCache } from 'react-query'

import type { ReactQueryCacheProviderProps } from '../react'
import { hydrate } from './hydration'

export function useHydrate(queries: unknown) {
const queryCache = useQueryCache()
Expand All @@ -21,28 +17,11 @@ export function useHydrate(queries: unknown) {
}, [queryCache, queries])
}

interface HydratorProps {
dehydratedState?: unknown
export interface HydrateProps {
state?: unknown
}

const Hydrator: React.FC<HydratorProps> = ({ dehydratedState, children }) => {
useHydrate(dehydratedState)
export const Hydrate: React.FC<HydrateProps> = ({ state, children }) => {
useHydrate(state)
return children as React.ReactElement<any>
}

export interface HydrationCacheProviderProps
extends ReactQueryCacheProviderProps {
dehydratedState?: unknown
}

export const ReactQueryCacheProvider: React.FC<HydrationCacheProviderProps> = ({
dehydratedState,
children,
...rest
}) => {
return (
<CacheProvider {...rest}>
<Hydrator dehydratedState={dehydratedState}>{children}</Hydrator>
</CacheProvider>
)
}
50 changes: 21 additions & 29 deletions src/hydration/tests/react.test.tsx
Original file line number Diff line number Diff line change
@@ -1,12 +1,8 @@
import React from 'react'
import { render } from '@testing-library/react'

import {
ReactQueryCacheProvider as OriginalCacheProvider,
makeQueryCache,
useQuery,
} from '../..'
import { dehydrate, useHydrate, ReactQueryCacheProvider } from '../'
import { ReactQueryCacheProvider, makeQueryCache, useQuery } from '../..'
import { dehydrate, useHydrate, Hydrate } from '../'
import { waitForMs } from '../../react/tests/utils'

describe('React hydration', () => {
Expand Down Expand Up @@ -58,9 +54,9 @@ describe('React hydration', () => {
}

const rendered = render(
<OriginalCacheProvider queryCache={clientQueryCache}>
<ReactQueryCacheProvider queryCache={clientQueryCache}>
<Page />
</OriginalCacheProvider>
</ReactQueryCacheProvider>
)

await waitForMs(10)
Expand All @@ -84,11 +80,10 @@ describe('React hydration', () => {
}

const rendered = render(
<ReactQueryCacheProvider
queryCache={clientQueryCache}
dehydratedState={dehydratedState}
>
<Page queryKey={'string'} />
<ReactQueryCacheProvider queryCache={clientQueryCache}>
<Hydrate state={dehydratedState}>
<Page queryKey={'string'} />
</Hydrate>
</ReactQueryCacheProvider>
)

Expand All @@ -104,12 +99,11 @@ describe('React hydration', () => {
intermediateCache.clear({ notify: false })

rendered.rerender(
<ReactQueryCacheProvider
queryCache={clientQueryCache}
dehydratedState={dehydrated}
>
<Page queryKey={'string'} />
<Page queryKey={'added string'} />
<ReactQueryCacheProvider queryCache={clientQueryCache}>
<Hydrate state={dehydrated}>
<Page queryKey={'string'} />
<Page queryKey={'added string'} />
</Hydrate>
</ReactQueryCacheProvider>
)

Expand Down Expand Up @@ -137,11 +131,10 @@ describe('React hydration', () => {
}

const rendered = render(
<ReactQueryCacheProvider
queryCache={clientQueryCache}
dehydratedState={dehydratedState}
>
<Page />
<ReactQueryCacheProvider queryCache={clientQueryCache}>
<Hydrate state={dehydratedState}>
<Page />
</Hydrate>
</ReactQueryCacheProvider>
)

Expand All @@ -151,11 +144,10 @@ describe('React hydration', () => {
const newClientQueryCache = makeQueryCache()

rendered.rerender(
<ReactQueryCacheProvider
queryCache={newClientQueryCache}
dehydratedState={dehydratedState}
>
<Page />
<ReactQueryCacheProvider queryCache={newClientQueryCache}>
<Hydrate state={dehydratedState}>
<Page />
</Hydrate>
</ReactQueryCacheProvider>
)

Expand Down
Loading