Skip to content

Commit d934fad

Browse files
authored
Merge pull request #2844 from reduxjs/bugfix/forceRefetch-obj-args
2 parents 270a14f + db3ed1e commit d934fad

File tree

2 files changed

+67
-1
lines changed

2 files changed

+67
-1
lines changed

packages/toolkit/src/query/react/buildHooks.ts

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ import type { UninitializedValue } from './constants'
5454
import { UNINITIALIZED_VALUE } from './constants'
5555
import { useShallowStableValue } from './useShallowStableValue'
5656
import type { BaseQueryFn } from '../baseQueryTypes'
57+
import { defaultSerializeQueryArgs } from '../defaultSerializeQueryArgs'
5758

5859
// Copy-pasted from React-Redux
5960
export const useIsomorphicLayoutEffect =
@@ -688,7 +689,12 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
688689
const dispatch = useDispatch<ThunkDispatch<any, any, AnyAction>>()
689690
const stableArg = useStableQueryArgs(
690691
skip ? skipToken : arg,
691-
serializeQueryArgs,
692+
// Even if the user provided a per-endpoint `serializeQueryArgs` with
693+
// a consistent return value, _here_ we want to use the default behavior
694+
// so we can tell if _anything_ actually changed. Otherwise, we can end up
695+
// with a case where the query args did change but the serialization doesn't,
696+
// and then we never try to initiate a refetch.
697+
defaultSerializeQueryArgs,
692698
context.endpointDefinitions[name],
693699
name
694700
)

packages/toolkit/src/query/tests/buildHooks.test.tsx

Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,11 @@ import { delay } from '../../utils'
3737
// This can be used to test how many renders happen due to data changes or
3838
// the refetching behavior of components.
3939
let amount = 0
40+
let nextItemId = 0
41+
42+
interface Item {
43+
id: number
44+
}
4045

4146
const api = createApi({
4247
baseQuery: async (arg: any) => {
@@ -54,6 +59,15 @@ const api = createApi({
5459
}
5560
}
5661

62+
if (arg?.body && 'listItems' in arg.body) {
63+
const items: Item[] = []
64+
for (let i = 0; i < 3; i++) {
65+
const item = { id: nextItemId++ }
66+
items.push(item)
67+
}
68+
return { data: items }
69+
}
70+
5771
return {
5872
data: arg?.body ? { ...arg.body, ...(amount ? { amount } : {}) } : {},
5973
}
@@ -85,6 +99,23 @@ const api = createApi({
8599
getError: build.query({
86100
query: (query) => '/error',
87101
}),
102+
listItems: build.query<Item[], { pageNumber: number }>({
103+
serializeQueryArgs: ({ endpointName }) => {
104+
return endpointName
105+
},
106+
query: ({ pageNumber }) => ({
107+
url: `items?limit=1&offset=${pageNumber}`,
108+
body: {
109+
listItems: true,
110+
},
111+
}),
112+
merge: (currentCache, newItems) => {
113+
currentCache.push(...newItems)
114+
},
115+
forceRefetch: ({ currentArg, previousArg }) => {
116+
return true
117+
},
118+
}),
88119
}),
89120
})
90121

@@ -589,6 +620,35 @@ describe('hooks tests', () => {
589620
)
590621
})
591622

623+
test(`useQuery refetches when query args object changes even if serialized args don't change`, async () => {
624+
function ItemList() {
625+
const [pageNumber, setPageNumber] = React.useState(0)
626+
const { data = [] } = api.useListItemsQuery({ pageNumber })
627+
628+
const renderedItems = data.map((item) => (
629+
<li key={item.id}>ID: {item.id}</li>
630+
))
631+
return (
632+
<div>
633+
<button onClick={() => setPageNumber(pageNumber + 1)}>
634+
Next Page
635+
</button>
636+
<ul>{renderedItems}</ul>
637+
</div>
638+
)
639+
}
640+
641+
render(<ItemList />, { wrapper: storeRef.wrapper })
642+
643+
await screen.findByText('ID: 0')
644+
645+
await act(async () => {
646+
screen.getByText('Next Page').click()
647+
})
648+
649+
await screen.findByText('ID: 3')
650+
})
651+
592652
describe('api.util.resetApiState resets hook', () => {
593653
test('without `selectFromResult`', async () => {
594654
const { result } = renderHook(() => api.endpoints.getUser.useQuery(5), {

0 commit comments

Comments
 (0)