Skip to content

Commit

Permalink
use useSerializedStableValue for value comparison (#1533)
Browse files Browse the repository at this point in the history
Co-authored-by: Matt Sutkowski <msutkowski@gmail.com>
  • Loading branch information
phryneas and msutkowski authored Oct 1, 2021
1 parent 18ef51d commit a44511e
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 13 deletions.
24 changes: 20 additions & 4 deletions packages/toolkit/src/query/react/buildHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -31,8 +31,9 @@ import type {
QueryActionCreatorResult,
MutationActionCreatorResult,
} from '@reduxjs/toolkit/dist/query/core/buildInitiate'
import type { SerializeQueryArgs } from '@reduxjs/toolkit/dist/query/defaultSerializeQueryArgs'
import { shallowEqual } from 'react-redux'
import type { Api } from '@reduxjs/toolkit/dist/query/apiTypes'
import type { Api, ApiContext } from '@reduxjs/toolkit/dist/query/apiTypes'
import type {
Id,
NoInfer,
Expand All @@ -45,9 +46,10 @@ import type {
PrefetchOptions,
} from '@reduxjs/toolkit/dist/query/core/module'
import type { ReactHooksModuleOptions } from './module'
import { useShallowStableValue } from './useShallowStableValue'
import { useStableQueryArgs } from './useSerializedStableValue'
import type { UninitializedValue } from './constants'
import { UNINITIALIZED_VALUE } from './constants'
import { useShallowStableValue } from './useShallowStableValue'

// Copy-pasted from React-Redux
export const useIsomorphicLayoutEffect =
Expand Down Expand Up @@ -482,9 +484,13 @@ type GenericPrefetchThunk = (
export function buildHooks<Definitions extends EndpointDefinitions>({
api,
moduleOptions: { batch, useDispatch, useSelector, useStore },
serializeQueryArgs,
context,
}: {
api: Api<any, Definitions, any, any, CoreModule>
moduleOptions: Required<ReactHooksModuleOptions>
serializeQueryArgs: SerializeQueryArgs<any>
context: ApiContext<Definitions>
}) {
return { buildQueryHooks, buildMutationHook, usePrefetch }

Expand Down Expand Up @@ -523,7 +529,12 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
Definitions
>
const dispatch = useDispatch<ThunkDispatch<any, any, AnyAction>>()
const stableArg = useShallowStableValue(skip ? skipToken : arg)
const stableArg = useStableQueryArgs(
skip ? skipToken : arg,
serializeQueryArgs,
context.endpointDefinitions[name],
name
)
const stableSubscriptionOptions = useShallowStableValue({
refetchOnReconnect,
refetchOnFocus,
Expand Down Expand Up @@ -658,7 +669,12 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
QueryDefinition<any, any, any, any, any>,
Definitions
>
const stableArg = useShallowStableValue(skip ? skipToken : arg)
const stableArg = useStableQueryArgs(
skip ? skipToken : arg,
serializeQueryArgs,
context.endpointDefinitions[name],
name
)

const lastValue = useRef<any>()

Expand Down
4 changes: 3 additions & 1 deletion packages/toolkit/src/query/react/module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,7 +109,7 @@ export const reactHooksModule = ({
useStore = rrUseStore,
}: ReactHooksModuleOptions = {}): Module<ReactHooksModule> => ({
name: reactHooksModuleName,
init(api, options, context) {
init(api, { serializeQueryArgs }, context) {
const anyApi = api as any as Api<
any,
Record<string, any>,
Expand All @@ -120,6 +120,8 @@ export const reactHooksModule = ({
const { buildQueryHooks, buildMutationHook, usePrefetch } = buildHooks({
api,
moduleOptions: { batch, useDispatch, useSelector, useStore },
serializeQueryArgs,
context,
})
safeAssign(anyApi, { usePrefetch })
safeAssign(context, { batch })
Expand Down
31 changes: 31 additions & 0 deletions packages/toolkit/src/query/react/useSerializedStableValue.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
import { useEffect, useRef, useMemo } from 'react'
import type { SerializeQueryArgs } from '@reduxjs/toolkit/dist/query/defaultSerializeQueryArgs'
import type { EndpointDefinition } from '@reduxjs/toolkit/dist/query/endpointDefinitions'

export function useStableQueryArgs<T>(
queryArgs: T,
serialize: SerializeQueryArgs<any>,
endpointDefinition: EndpointDefinition<any, any, any, any>,
endpointName: string
) {
const incoming = useMemo(
() => ({
queryArgs,
serialized:
typeof queryArgs == 'object'
? serialize({ queryArgs, endpointDefinition, endpointName })
: queryArgs,
}),
[queryArgs, serialize, endpointDefinition, endpointName]
)
const cache = useRef(incoming)
useEffect(() => {
if (cache.current.serialized !== incoming.serialized) {
cache.current = incoming
}
}, [incoming])

return cache.current.serialized === incoming.serialized
? cache.current.queryArgs
: queryArgs
}
11 changes: 6 additions & 5 deletions packages/toolkit/src/query/tests/buildHooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -494,18 +494,19 @@ describe('hooks tests', () => {

let { unmount } = render(<User />, { wrapper: storeRef.wrapper })

expect(screen.getByTestId('isFetching').textContent).toBe('false')

// skipped queries do nothing by default, so we need to toggle that to get a cached result
fireEvent.click(screen.getByText('change skip'))

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

await waitFor(() => {
expect(screen.getByTestId('amount').textContent).toBe('1')
)
expect(screen.getByTestId('isFetching').textContent).toBe('false')
})

unmount()

Expand Down
8 changes: 5 additions & 3 deletions packages/toolkit/src/query/tests/buildThunks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -156,12 +156,14 @@ describe('re-triggering behavior on arg change', () => {
}
})

test('re-trigger every time on deeper value changes', async () => {
test('re-triggers every time on deeper value changes', async () => {
const name = 'Tim'

const { result, rerender, waitForNextUpdate } = renderHook(
(props) => getUser.useQuery(props),
{
wrapper: withProvider(store),
initialProps: { person: { name: 'Tim' } },
initialProps: { person: { name } },
}
)

Expand All @@ -171,7 +173,7 @@ describe('re-triggering behavior on arg change', () => {
expect(spy).toHaveBeenCalledTimes(1)

for (let x = 1; x < 3; x++) {
rerender({ person: { name: 'Tim' } })
rerender({ person: { name: name + x } })
// @ts-ignore
while (result.current.status === 'pending') {
await waitForNextUpdate()
Expand Down

0 comments on commit a44511e

Please sign in to comment.