Skip to content

Commit

Permalink
feat: include meta in query and mutation hydration (#5733)
Browse files Browse the repository at this point in the history
* feat: include `meta` in query and mutation hydration

* test: tests for meta hydration

* chore: stabilize a flaky test

* chore: stabilize another flaky test
  • Loading branch information
TkDodo authored Jul 18, 2023
1 parent 69f59b1 commit 3c9dc0f
Show file tree
Hide file tree
Showing 3 changed files with 131 additions and 11 deletions.
8 changes: 8 additions & 0 deletions packages/query-core/src/hydration.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@ import type { QueryClient } from './queryClient'
import type { Query, QueryState } from './query'
import type {
MutationKey,
MutationMeta,
MutationOptions,
QueryKey,
QueryMeta,
QueryOptions,
} from './types'
import type { Mutation, MutationState } from './mutation'
Expand All @@ -25,12 +27,14 @@ export interface HydrateOptions {
interface DehydratedMutation {
mutationKey?: MutationKey
state: MutationState
meta?: MutationMeta
}

interface DehydratedQuery {
queryHash: string
queryKey: QueryKey
state: QueryState
meta?: QueryMeta
}

export interface DehydratedState {
Expand All @@ -44,6 +48,7 @@ function dehydrateMutation(mutation: Mutation): DehydratedMutation {
return {
mutationKey: mutation.options.mutationKey,
state: mutation.state,
...(mutation.meta && { meta: mutation.meta }),
}
}

Expand All @@ -56,6 +61,7 @@ function dehydrateQuery(query: Query): DehydratedQuery {
state: query.state,
queryKey: query.queryKey,
queryHash: query.queryHash,
...(query.meta && { meta: query.meta }),
}
}

Expand Down Expand Up @@ -115,6 +121,7 @@ export function hydrate(
{
...options?.defaultOptions?.mutations,
mutationKey: dehydratedMutation.mutationKey,
meta: dehydratedMutation.meta,
},
dehydratedMutation.state,
)
Expand Down Expand Up @@ -145,6 +152,7 @@ export function hydrate(
...options?.defaultOptions?.queries,
queryKey: dehydratedQuery.queryKey,
queryHash: dehydratedQuery.queryHash,
meta: dehydratedQuery.meta,
},
dehydratedQueryState,
)
Expand Down
113 changes: 112 additions & 1 deletion packages/query-core/src/tests/hydration.test.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { vi } from 'vitest'
import { expect, vi } from 'vitest'
import { QueryCache } from '../queryCache'
import { dehydrate, hydrate } from '../hydration'
import { MutationCache } from '../mutationCache'
import {
createQueryClient,
executeMutation,
Expand Down Expand Up @@ -557,4 +558,114 @@ describe('dehydration and rehydration', () => {
hydrationCache.find({ queryKey: ['string'] })?.state.fetchStatus,
).toBe('idle')
})

test('should dehydrate and hydrate meta for queries', async () => {
const queryCache = new QueryCache()
const queryClient = createQueryClient({ queryCache })
await queryClient.prefetchQuery({
queryKey: ['meta'],
queryFn: () => Promise.resolve('meta'),
meta: {
some: 'meta',
},
})
await queryClient.prefetchQuery({
queryKey: ['no-meta'],
queryFn: () => Promise.resolve('no-meta'),
})

const dehydrated = dehydrate(queryClient)

expect(
dehydrated.queries.find((q) => q.queryHash === '["meta"]')?.meta,
).toEqual({
some: 'meta',
})

expect(
dehydrated.queries.find((q) => q.queryHash === '["no-meta"]')?.meta,
).toEqual(undefined)

expect(
Object.keys(
dehydrated.queries.find((q) => q.queryHash === '["no-meta"]')!,
),
).not.toEqual(expect.arrayContaining(['meta']))

const stringified = JSON.stringify(dehydrated)

// ---

const parsed = JSON.parse(stringified)
const hydrationCache = new QueryCache()
const hydrationClient = createQueryClient({
queryCache: hydrationCache,
})
hydrate(hydrationClient, parsed)
expect(hydrationCache.find({ queryKey: ['meta'] })?.meta).toEqual({
some: 'meta',
})
expect(hydrationCache.find({ queryKey: ['no-meta'] })?.meta).toEqual(
undefined,
)
})

test('should dehydrate and hydrate meta for mutations', async () => {
const mutationCache = new MutationCache()
const queryClient = createQueryClient({ mutationCache })

await executeMutation(
queryClient,
{
mutationKey: ['meta'],
mutationFn: () => Promise.resolve('meta'),
meta: {
some: 'meta',
},
},
undefined,
)

await executeMutation(
queryClient,
{
mutationKey: ['no-meta'],
mutationFn: () => Promise.resolve('no-meta'),
},
undefined,
)

const dehydrated = dehydrate(queryClient, {
shouldDehydrateMutation: () => true,
})

expect(Object.keys(dehydrated.mutations[0]!)).toEqual(
expect.arrayContaining(['meta']),
)
expect(dehydrated.mutations[0]?.meta).toEqual({
some: 'meta',
})

expect(Object.keys(dehydrated.mutations[1]!)).not.toEqual(
expect.arrayContaining(['meta']),
)
expect(dehydrated.mutations[1]?.meta).toEqual(undefined)

const stringified = JSON.stringify(dehydrated)

// ---

const parsed = JSON.parse(stringified)
const hydrationCache = new MutationCache()
const hydrationClient = createQueryClient({
mutationCache: hydrationCache,
})
hydrate(hydrationClient, parsed)
expect(hydrationCache.find({ mutationKey: ['meta'] })?.meta).toEqual({
some: 'meta',
})
expect(hydrationCache.find({ mutationKey: ['no-meta'] })?.meta).toEqual(
undefined,
)
})
})
21 changes: 11 additions & 10 deletions packages/react-query/src/__tests__/useQuery.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2603,24 +2603,24 @@ describe('useQuery', () => {
refetchOnWindowFocus: 'always',
})
states.push(state)
return null
return (
<div>
<div>
data: {state.data}, isFetching: {String(state.isFetching)}
</div>
</div>
)
}

renderWithClient(queryClient, <Page />)
const rendered = renderWithClient(queryClient, <Page />)

await sleep(20)
await waitFor(() => rendered.getByText('data: 0, isFetching: false'))

act(() => {
window.dispatchEvent(new Event('visibilitychange'))
})

await sleep(20)

await waitFor(() => expect(states.length).toBe(4))
expect(states[0]).toMatchObject({ data: undefined, isFetching: true })
expect(states[1]).toMatchObject({ data: 0, isFetching: false })
expect(states[2]).toMatchObject({ data: 0, isFetching: true })
expect(states[3]).toMatchObject({ data: 1, isFetching: false })
await waitFor(() => rendered.getByText('data: 1, isFetching: false'))
})

it('should calculate focus behaviour for `refetchOnWindowFocus` depending on function', async () => {
Expand Down Expand Up @@ -5458,6 +5458,7 @@ describe('useQuery', () => {
await waitFor(() => rendered.getByText('failureReason: failed1'))

const onlineMock = mockNavigatorOnLine(false)
window.dispatchEvent(new Event('offline'))

await sleep(20)

Expand Down

0 comments on commit 3c9dc0f

Please sign in to comment.