Skip to content

Commit 7204cdd

Browse files
committed
proof of concept for handling bigint in useQuerySubscription
1 parent 1afcdd4 commit 7204cdd

File tree

3 files changed

+70
-4
lines changed

3 files changed

+70
-4
lines changed

packages/toolkit/src/query/defaultSerializeQueryArgs.ts

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,38 @@ export const defaultSerializeQueryArgs: SerializeQueryArgs<any> = ({
3636
return `${endpointName}(${serialized})`
3737
}
3838

39+
export const bigIntSafeSerializeQueryArgs: SerializeQueryArgs<any> = ({
40+
endpointName,
41+
queryArgs,
42+
}) => {
43+
let serialized = ''
44+
45+
const cached = cache?.get(queryArgs)
46+
47+
if (typeof cached === 'string') {
48+
serialized = cached
49+
} else {
50+
const stringified = JSON.stringify(queryArgs, (key, value) => {
51+
value = typeof value === 'bigint' ? { $bigint: value.toString() } : value
52+
value = isPlainObject(value)
53+
? Object.keys(value)
54+
.sort()
55+
.reduce<any>((acc, key) => {
56+
acc[key] = (value as any)[key]
57+
return acc
58+
}, {})
59+
: value
60+
return value
61+
})
62+
if (isPlainObject(queryArgs)) {
63+
cache?.set(queryArgs, stringified)
64+
}
65+
serialized = stringified
66+
}
67+
// Sort the object keys before stringifying, to prevent useQuery({ a: 1, b: 2 }) having a different cache key than useQuery({ b: 2, a: 1 })
68+
return `${endpointName}(${serialized})`
69+
}
70+
3971
export type SerializeQueryArgs<QueryArgs, ReturnType = string> = (_: {
4072
queryArgs: QueryArgs
4173
endpointDefinition: EndpointDefinition<any, any, any, any>

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

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,7 @@ import {
4545
import { shallowEqual } from 'react-redux'
4646
import type { BaseQueryFn } from '../baseQueryTypes'
4747
import type { SubscriptionSelectors } from '../core/buildMiddleware/types'
48-
import { defaultSerializeQueryArgs } from '../defaultSerializeQueryArgs'
48+
import { bigIntSafeSerializeQueryArgs } from '../defaultSerializeQueryArgs'
4949
import type { UninitializedValue } from './constants'
5050
import { UNINITIALIZED_VALUE } from './constants'
5151
import type { ReactHooksModuleOptions } from './module'
@@ -768,7 +768,7 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
768768
// so we can tell if _anything_ actually changed. Otherwise, we can end up
769769
// with a case where the query args did change but the serialization doesn't,
770770
// and then we never try to initiate a refetch.
771-
defaultSerializeQueryArgs,
771+
bigIntSafeSerializeQueryArgs,
772772
context.endpointDefinitions[name],
773773
name,
774774
)

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

Lines changed: 36 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -100,7 +100,7 @@ const api = createApi({
100100
getError: build.query({
101101
query: () => '/error',
102102
}),
103-
listItems: build.query<Item[], { pageNumber: number }>({
103+
listItems: build.query<Item[], { pageNumber: number | bigint }>({
104104
serializeQueryArgs: ({ endpointName }) => {
105105
return endpointName
106106
},
@@ -151,6 +151,7 @@ beforeEach(() => {
151151
})
152152

153153
afterEach(() => {
154+
nextItemId = 0
154155
amount = 0
155156
listenerMiddleware.clearListeners()
156157
})
@@ -630,7 +631,40 @@ describe('hooks tests', () => {
630631
test(`useQuery refetches when query args object changes even if serialized args don't change`, async () => {
631632
function ItemList() {
632633
const [pageNumber, setPageNumber] = useState(0)
633-
const { data = [] } = api.useListItemsQuery({ pageNumber })
634+
const { data = [] } = api.useListItemsQuery({
635+
pageNumber: pageNumber,
636+
})
637+
638+
const renderedItems = data.map((item) => (
639+
<li key={item.id}>ID: {item.id}</li>
640+
))
641+
return (
642+
<div>
643+
<button onClick={() => setPageNumber(pageNumber + 1)}>
644+
Next Page
645+
</button>
646+
<ul>{renderedItems}</ul>
647+
</div>
648+
)
649+
}
650+
651+
render(<ItemList />, { wrapper: storeRef.wrapper })
652+
653+
await screen.findByText('ID: 0')
654+
655+
await act(async () => {
656+
screen.getByText('Next Page').click()
657+
})
658+
659+
await screen.findByText('ID: 3')
660+
})
661+
662+
test(`useQuery gracefully handles bigint types`, async () => {
663+
function ItemList() {
664+
const [pageNumber, setPageNumber] = useState(0)
665+
const { data = [] } = api.useListItemsQuery({
666+
pageNumber: BigInt(pageNumber),
667+
})
634668

635669
const renderedItems = data.map((item) => (
636670
<li key={item.id}>ID: {item.id}</li>

0 commit comments

Comments
 (0)