Skip to content

Commit

Permalink
Only pass initialPageParams option through query hook
Browse files Browse the repository at this point in the history
  • Loading branch information
markerikson committed Oct 27, 2024
1 parent 1ce26cb commit ffafe62
Show file tree
Hide file tree
Showing 2 changed files with 124 additions and 71 deletions.
44 changes: 18 additions & 26 deletions packages/toolkit/src/query/react/buildHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -758,7 +758,9 @@ export type LazyInfiniteQueryTrigger<
): InfiniteQueryActionCreatorResult<D>
}

interface UseInfiniteQuerySubscriptionOptions extends SubscriptionOptions {
interface UseInfiniteQuerySubscriptionOptions<
D extends InfiniteQueryDefinition<any, any, any, any, any>,
> extends SubscriptionOptions {
/**
* Prevents a query from automatically running.
*
Expand Down Expand Up @@ -801,6 +803,7 @@ interface UseInfiniteQuerySubscriptionOptions extends SubscriptionOptions {
* If you specify this option alongside `skip: true`, this **will not be evaluated** until `skip` is false.
*/
refetchOnMountOrArgChange?: boolean | number
initialPageParam?: PageParamFrom<D>
}

export type TypedUseInfiniteQuerySubscription<
Expand Down Expand Up @@ -852,19 +855,14 @@ export type UseInfiniteQuery<
D extends InfiniteQueryDefinition<any, any, any, any, any>,
> = <R extends Record<string, any> = UseInfiniteQueryStateDefaultResult<D>>(
arg: InfiniteQueryArgFrom<D> | SkipToken,
options?: UseInfiniteQuerySubscriptionOptions &
UseInfiniteQueryStateOptions<D, R> &
InfiniteQueryConfigOptions<ResultTypeFrom<D>, PageParamFrom<D>>,
options?: UseInfiniteQuerySubscriptionOptions<D> &
UseInfiniteQueryStateOptions<D, R>,
) => UseInfiniteQueryHookResult<D, R>

export type UseInfiniteQueryState<
D extends InfiniteQueryDefinition<any, any, any, any, any>,
> = <R extends Record<string, any> = UseInfiniteQueryStateDefaultResult<D>>(
arg: QueryArgFrom<D> | SkipToken,
InfiniteQueryConfigOptions: InfiniteQueryConfigOptions<
ResultTypeFrom<D>,
PageParamFrom<D>
>,
options?: UseInfiniteQueryStateOptions<D, R>,
) => UseInfiniteQueryStateResult<D, R>

Expand All @@ -888,7 +886,7 @@ export type UseInfiniteQuerySubscription<
D extends InfiniteQueryDefinition<any, any, any, any, any>,
> = (
arg: QueryArgFrom<D> | SkipToken,
options?: UseInfiniteQuerySubscriptionOptions,
options?: UseInfiniteQuerySubscriptionOptions<D>,
) => readonly [
LazyInfiniteQueryTrigger<D>,
QueryArgFrom<D> | UninitializedValue,
Expand Down Expand Up @@ -1706,6 +1704,7 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
skip = false,
pollingInterval = 0,
skipPollingIfUnfocused = false,
initialPageParam,
} = {},
) => {
const { initiate } = api.endpoints[
Expand Down Expand Up @@ -1806,6 +1805,7 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
lastPromise?.unsubscribe()
const promise = dispatch(
initiate(stableArg, {
initialPageParam,
subscriptionOptions: stableSubscriptionOptions,
forceRefetch: refetchOnMountOrArgChange,
}),
Expand All @@ -1822,6 +1822,7 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
stableArg,
stableSubscriptionOptions,
subscriptionRemoved,
initialPageParam,
])

const subscriptionOptionsRef = useRef(stableSubscriptionOptions)
Expand Down Expand Up @@ -1861,7 +1862,6 @@ export function buildHooks<Definitions extends EndpointDefinitions>({

const useInfiniteQueryState: UseInfiniteQueryState<any> = (
arg: any,
{ getNextPageParam, getPreviousPageParam },
{ skip = false, selectFromResult, trigger } = {},
) => {
const { select, initiate } = api.endpoints[
Expand Down Expand Up @@ -1932,22 +1932,14 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
useInfiniteQuerySubscription,
useInfiniteQuery(arg, options) {
const [trigger] = useInfiniteQuerySubscription(arg, options)
const queryStateResults = useInfiniteQueryState(
arg,
{
initialPageParam: options?.initialPageParam!,
getNextPageParam: options?.getNextPageParam!,
getPreviousPageParam: options?.getPreviousPageParam,
},
{
selectFromResult:
arg === skipToken || options?.skip
? undefined
: noPendingQueryStateSelector,
trigger,
...options,
},
)
const queryStateResults = useInfiniteQueryState(arg, {
selectFromResult:
arg === skipToken || options?.skip
? undefined
: noPendingQueryStateSelector,
trigger,
...options,
})

const {
data,
Expand Down
151 changes: 106 additions & 45 deletions packages/toolkit/src/query/tests/buildHooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -162,6 +162,8 @@ afterEach(() => {
nextItemId = 0
amount = 0
listenerMiddleware.clearListeners()

server.resetHandlers()
})

let getRenderCount: () => number = () => 0
Expand Down Expand Up @@ -1498,6 +1500,14 @@ describe('hooks tests', () => {
lastPageParam,
allPageParams,
) => lastPageParam + 1,
getPreviousPageParam: (
firstPage,
allPages,
firstPageParam,
allPageParams,
) => {
return firstPageParam > 0 ? firstPageParam - 1 : undefined
},
},
query(pageParam) {
return `https://example.com/listItems?page=${pageParam}`
Expand All @@ -1507,57 +1517,66 @@ describe('hooks tests', () => {
})

function PokemonList({
arg = 'fire',
initialPageParam = 0,
}: {
arg?: string
initialPageParam?: number
}) {
const { data, isFetching, isUninitialized, fetchNextPage } =
pokemonApi.endpoints.getInfinitePokemon.useInfiniteQuery('a', {
initialPageParam,
getNextPageParam: (
lastPage,
allPages,
// Page param type should be `number`
lastPageParam,
allPageParams,
) => lastPageParam + 1,
})
const {
data,
isFetching,
isUninitialized,
fetchNextPage,
fetchPreviousPage,
} = pokemonApi.endpoints.getInfinitePokemon.useInfiniteQuery(arg, {
initialPageParam,
})

const handlePreviousPage = async () => {
const res = await fetchPreviousPage()
}

const handleClick = async () => {
const promise = fetchNextPage()
const res = await promise
const handleNextPage = async () => {
const res = await fetchNextPage()
}

return (
<div>
<div data-testid="isUninitialized">{String(isUninitialized)}</div>
<div data-testid="isFetching">{String(isFetching)}</div>
<div>Type: {arg}</div>
<div data-testid="data">
{data?.pages.map((page: any, i: number | null | undefined) => (
<div key={i}>{JSON.stringify(page)}</div>
{data?.pages.map((page, i: number | null | undefined) => (
<div key={i}>{page.name}</div>
))}
</div>
<button data-testid="nextPage" onClick={() => handleClick()}>
<button data-testid="prevPage" onClick={() => handlePreviousPage()}>
nextPage
</button>
<button data-testid="nextPage" onClick={() => handleNextPage()}>
nextPage
</button>
</div>
)
}

server.use(
http.get('https://example.com/listItems', ({ request }) => {
const url = new URL(request.url)
const pageString = url.searchParams.get('page')
const pageNum = parseInt(pageString || '0')

const results: Pokemon = {
id: `${pageNum}`,
name: `Pokemon ${pageNum}`,
}
beforeEach(() => {
server.use(
http.get('https://example.com/listItems', ({ request }) => {
const url = new URL(request.url)
const pageString = url.searchParams.get('page')
const pageNum = parseInt(pageString || '0')

const results: Pokemon = {
id: `${pageNum}`,
name: `Pokemon ${pageNum}`,
}

return HttpResponse.json(results)
}),
)
return HttpResponse.json(results)
}),
)
})

test('useInfiniteQuery fetchNextPage Trigger', async () => {
const storeRef = setupApiStore(pokemonApi, undefined, {
Expand All @@ -1567,34 +1586,76 @@ describe('hooks tests', () => {
const checkNumQueries = (count: number) => {
const cacheEntries = Object.keys(storeRef.store.getState().api.queries)
const queries = cacheEntries.length
console.log('queries', queries, storeRef.store.getState().api.queries)
//console.log('queries', queries, storeRef.store.getState().api.queries)

expect(queries).toBe(count)
}

render(<PokemonList />, { wrapper: storeRef.wrapper })
const checkPageRows = (type: string, ids: number[]) => {
expect(screen.getByText(`Type: ${type}`)).toBeTruthy()
for (const id of ids) {
expect(screen.getByText(`Pokemon ${id}`)).toBeTruthy()
}
}

async function waitForFetch() {
await waitFor(() =>
expect(screen.getByTestId('isFetching').textContent).toBe('true'),
)
await waitFor(() =>
expect(screen.getByTestId('isFetching').textContent).toBe('false'),
)
}

const utils = render(<PokemonList />, { wrapper: storeRef.wrapper })
expect(screen.getByTestId('data').textContent).toBe('')
checkNumQueries(1)

await waitFor(() =>
expect(screen.getByTestId('isUninitialized').textContent).toBe('false'),
)
await waitFor(() =>
expect(screen.getByTestId('isFetching').textContent).toBe('false'),
)

// Initial load
await waitForFetch()
checkNumQueries(1)
checkPageRows('fire', [0])

act(() => {
fireEvent.click(screen.getByTestId('nextPage'))
})
await waitFor(() =>
expect(screen.getByTestId('isFetching').textContent).toBe('false'),
)
checkNumQueries(1)
await waitFor(() =>
expect(screen.getByTestId('isFetching').textContent).toBe('false'),
)
expect(screen.getByTestId('data').textContent).toBe(
'{"id":"0","name":"Pokemon 0"}{"id":"1","name":"Pokemon 1"}',
)

await waitForFetch()

// Added one page
checkPageRows('fire', [0, 1])

act(() => {
fireEvent.click(screen.getByTestId('nextPage'))
})
await waitForFetch()

checkPageRows('fire', [0, 1, 2])

utils.rerender(<PokemonList arg="water" initialPageParam={3} />)

await waitForFetch()

checkNumQueries(2)
checkPageRows('water', [3])

act(() => {
fireEvent.click(screen.getByTestId('nextPage'))
})
await waitForFetch()

checkPageRows('water', [3, 4])

act(() => {
fireEvent.click(screen.getByTestId('prevPage'))
})
await waitForFetch()

checkPageRows('water', [2, 3, 4])
})
})

Expand Down

0 comments on commit ffafe62

Please sign in to comment.