-
Notifications
You must be signed in to change notification settings - Fork 31
Expand documentation on SSR hydration with SvelteKit #68
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
base: main
Are you sure you want to change the base?
Conversation
Actually, i discovered the approach I used in the example doesn't quite work as expected. I did figure it out though, will update very soon. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
...
@amen-souissi So I've discovered 2 things, one is that SvelteKit does the (de)hydration automatically when using the The other is that unless I'm missing something, using svelte-query's hydration is a bit inelegant for a few reasons:
This is the sort of thing we're ending up with: // __layout.svelte
<script context="module">
import { hydrate, QueryClient } from '@sveltestack/svelte-query';
export function load({ stuff, session }) {
const { dehydratedState } = session; // dehydratedState is set in the session during SSR
const queryClient = new QueryClient();
hydrate(queryClient, dehydratedState); //
stuff.queryClient = queryClient; // save a reference to the hydrated queryClient instance so it can be used in other components' `load`
return { props: {queryClient}};
}
<script>
import { QueryClientProvider } from '@sveltestack/svelte-query';
export const queryClient;
</script>
<QueryClientProvider client={queryClient}>
<slot />
</QueryClientProvider> // pages/posts.svelte
<script context="module">
import { dehydrate, QueryClient, useQuery } from 'react-query';
export async function load({ session }) {
const queryClient = stuff.queryClient; // access the queryClient, during SSR it won't have been hydrated but when loaded in the browser it will
await queryClient.prefetchQuery('posts', getPosts)
session.dehydratedState = dehydrate(queryClient); // save the dehydratedState in the session during SSR so it can be accessed in the browser
}
}
</script>
<script>
const queryResult = useQuery('posts', getPosts) // by the time we get here hydration will have happend
</script> |
@Jakeii super helpful, thanks! Are you able to build successfully when you import
This appears to work instead, with the caveat that you lose the type declarations: import { hydrate } from '@sveltestack/svelte-query/svelte/queryCore/hydration'; |
strange, building works fine for me, what version of sveltekit are you using? |
1.0.0-next.250 I haven't investigated because the workaround is simple enough, but it is strange, indeed. |
Here is my solution. it work well with pagination. // __layout.svelte
<script lang="ts">
import { session } from '$app/stores';
import { Hydrate, QueryClient, QueryClientProvider } from '@sveltestack/svelte-query';
const queryClient = new QueryClient({
// If you don't pass staleTime. then fetching 1 page on first render.
// So pass 5 minutes for 1st render
defaultOptions: { queries: { refetchOnWindowFocus: false, staleTime: 60000 } }
});
</script>
<QueryClientProvider client={queryClient}>
// Get state from server from session
<Hydrate state={$session.dehydratedState}>
<slot />
</Hydrate>
</QueryClientProvider> // /consults/[page].svelte
<script lang="ts" context="module">
import { browser } from '$app/env';
import { useQuery, dehydrate, QueryClient, type QueryFunction } from '@sveltestack/svelte-query';
let origin = '';
export async function load({ url, params, session }) {
// Create the QueryClient on server only for prefetching
if (!browser) {
origin = url.origin;
const queryClient = new QueryClient();
await queryClient.prefetchQuery(`consults/${params.page}`, getConsults);
// Return stuff here isn't help to send state to `__layout.svelte`
// So using session for deliver it to client side `__layout.svelte`
session.dehydratedState = dehydrate(queryClient);
}
return {};
}
const getConsults: QueryFunction<GetConsultResponse, string> = async ({ queryKey }) => {
const pageStr = /consults\/(\d+)/.exec(queryKey[0])![1];
const page = Number(pageStr || '1');
const res = await fetch(`${origin}/api/consults/${page}`);
return res.json();
};
</script>
<script lang="ts">
import { page } from '$app/stores';
import { goto } from '$app/navigation';
$: pageNum = Number($page.params.page);
$: result = useQuery(`consults/${pageNum}`, getConsults);
function onSetPage(e: CustomEvent) {
goto(`/consults/${e.detail}`);
}
</script>
{#if $result.isLoading}
loading!
{:else}
done!
{/if} It does not fetching fist time on render 1 page. when click 2page. it fetching 2nd page. at last. click 1 page it fetching 1 page. but it does not fetch first render :) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
A year late isn't too late!
Ok but this is literally the only resource on SSR for Svelte Query.
... I hope it works?
Together with SvelteKit's [`load`](https://kit.svelte.dev/docs#loading), you can pass the data you fetch to `useQuery`'s' `initialData` option: | ||
|
||
```markdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
```markdown | |
```svelte |
### Using `initialData` | ||
|
||
Together with SvelteKit's [`load`](https://kit.svelte.dev/docs#loading), you can pass the data you fetch to `useQuery`'s' `initialData` option: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Together with SvelteKit's [`load`](https://kit.svelte.dev/docs#loading), you can pass the data you fetch to `useQuery`'s' `initialData` option: | |
Together with SvelteKit's [`load`](https://kit.svelte.dev/docs#loading), you can pass the data you fetch to `useQuery`'s `initialData` option: |
- Wrap your app component with `<Hydrate>` and pass it the `dehydratedState` prop from `pageProps` | ||
|
||
```markdown |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
```markdown | |
```svelte |
|
||
```markdown | ||
// __layout.svelte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// __layout.svelte | |
// +layout.svelte |
- Create a new `QueryClient` instance | ||
- Prefetch the data using the clients `prefetchQuery` method and wait for it to complete | ||
- Use `dehydrate` to dehydrate the query cache and pass it to the page via the `dehydratedState` prop. This is the same prop that the cache will be picked up from in your `__layout.svelte` |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
- Use `dehydrate` to dehydrate the query cache and pass it to the page via the `dehydratedState` prop. This is the same prop that the cache will be picked up from in your `__layout.svelte` | |
- Use `dehydrate` to dehydrate the query cache and pass it to the page via the `dehydratedState` prop. This is the same prop that the cache will be picked up from in your `+layout.svelte` |
// pages/posts.svelte | ||
<script context="module"> | ||
import { dehydrate, QueryClient, useQuery } from 'react-query'; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🤔
|
||
```markdown | ||
// pages/posts.svelte |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
// pages/posts.svelte | |
// pages/posts/+page.svelte |
import { Hydrate } from '@sveltestack/svelte-query/hydration' | ||
```markdown | ||
<script> | ||
import { Hydrate } from '@sveltestack/svelte-query' |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
import { Hydrate } from '@sveltestack/svelte-query' | |
import { Hydrate } from '@sveltestack/svelte-query' |
@@ -356,4 +356,4 @@ | |||
] | |||
} | |||
] | |||
} | |||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
} | |
} | |
} | ||
</script> | ||
<script> |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's a mix of <script>
and <script lang="ts">
; it would be nice if it could all be <script lang="ts">
so it can be useful to both JavaScript users with /** @type {} */
and to TypeScript users with import type {}
.
I saw this wasn't documented but found that it works great, so have expanded the documentation, very similar to the
next.js
/react-query
approach