diff --git a/packages/toolkit/src/query/react/buildHooks.ts b/packages/toolkit/src/query/react/buildHooks.ts index 5b68b9681c..5f23d7a4f7 100644 --- a/packages/toolkit/src/query/react/buildHooks.ts +++ b/packages/toolkit/src/query/react/buildHooks.ts @@ -18,6 +18,7 @@ import type { PrefetchOptions, QueryActionCreatorResult, QueryArgFrom, + QueryCacheKey, QueryDefinition, QueryKeys, QueryResultSelectorResult, @@ -255,7 +256,7 @@ export type UseLazyQuery> = < options?: SubscriptionOptions & Omit, 'skip'>, ) => [ LazyQueryTrigger, - UseQueryStateResult, + UseLazyQueryStateResult, UseLazyQueryLastPromiseInfo, ] @@ -267,6 +268,33 @@ export type TypedUseLazyQuery< QueryDefinition > +export type UseLazyQueryStateResult< + D extends QueryDefinition, + R = UseQueryStateDefaultResult, +> = UseQueryStateResult & { + /** + * Resets the hook state to its initial `uninitialized` state. + * This will also remove the last result from the cache. + */ + reset: () => void +} + +/** + * Helper type to manually type the result + * of the `useLazyQuery` hook in userland code. + */ +export type TypedUseLazyQueryStateResult< + ResultType, + QueryArg, + BaseQuery extends BaseQueryFn, + R = UseQueryStateDefaultResult< + QueryDefinition + >, +> = UseLazyQueryStateResult< + QueryDefinition, + R +> + export type LazyQueryTrigger> = { /** * Triggers a lazy query. @@ -317,7 +345,11 @@ export type UseLazyQuerySubscription< D extends QueryDefinition, > = ( options?: SubscriptionOptions, -) => readonly [LazyQueryTrigger, QueryArgFrom | UninitializedValue] +) => readonly [ + LazyQueryTrigger, + QueryArgFrom | UninitializedValue, + { reset: () => void }, +] export type TypedUseLazyQuerySubscription< ResultType, @@ -1162,6 +1194,16 @@ export function buildHooks({ [dispatch, initiate], ) + const reset = useCallback(() => { + if (promiseRef.current?.queryCacheKey) { + dispatch( + api.internalActions.removeQueryResult({ + queryCacheKey: promiseRef.current?.queryCacheKey as QueryCacheKey, + }), + ) + } + }, [dispatch]) + /* cleanup on unmount */ useEffect(() => { return () => { @@ -1176,7 +1218,10 @@ export function buildHooks({ } }, [arg, trigger]) - return useMemo(() => [trigger, arg] as const, [trigger, arg]) + return useMemo( + () => [trigger, arg, { reset }] as const, + [trigger, arg, reset], + ) } const useQueryState: UseQueryState = ( @@ -1249,7 +1294,7 @@ export function buildHooks({ useQuerySubscription, useLazyQuerySubscription, useLazyQuery(options) { - const [trigger, arg] = useLazyQuerySubscription(options) + const [trigger, arg, { reset }] = useLazyQuerySubscription(options) const queryStateResults = useQueryState(arg, { ...options, skip: arg === UNINITIALIZED_VALUE, @@ -1257,8 +1302,8 @@ export function buildHooks({ const info = useMemo(() => ({ lastArg: arg }), [arg]) return useMemo( - () => [trigger, queryStateResults, info], - [trigger, queryStateResults, info], + () => [trigger, { ...queryStateResults, reset }, info], + [trigger, queryStateResults, reset, info], ) }, useQuery(arg, options) { diff --git a/packages/toolkit/src/query/react/index.ts b/packages/toolkit/src/query/react/index.ts index 9816a915a0..e9fda233ae 100644 --- a/packages/toolkit/src/query/react/index.ts +++ b/packages/toolkit/src/query/react/index.ts @@ -28,6 +28,7 @@ export type { TypedUseQuerySubscription, TypedUseLazyQuerySubscription, TypedUseQueryStateOptions, + TypedUseLazyQueryStateResult, } from './buildHooks' export { UNINITIALIZED_VALUE } from './constants' export { createApi, reactHooksModule, reactHooksModuleName } diff --git a/packages/toolkit/src/query/tests/buildHooks.test.tsx b/packages/toolkit/src/query/tests/buildHooks.test.tsx index 6aa1e41518..36ffbe3239 100644 --- a/packages/toolkit/src/query/tests/buildHooks.test.tsx +++ b/packages/toolkit/src/query/tests/buildHooks.test.tsx @@ -1398,10 +1398,8 @@ describe('hooks tests', () => { test('useLazyQuery trigger promise returns the correctly updated data', async () => { const LazyUnwrapUseEffect = () => { - const [ - triggerGetIncrementedAmount, - { isFetching, isSuccess, isError, error, data }, - ] = api.endpoints.getIncrementedAmount.useLazyQuery() + const [triggerGetIncrementedAmount, { isFetching, isSuccess, data }] = + api.endpoints.getIncrementedAmount.useLazyQuery() type AmountData = { amount: number } | undefined @@ -1490,6 +1488,50 @@ describe('hooks tests', () => { expect(screen.getByText('Unwrap data: 2')).toBeTruthy() }) }) + + test('`reset` sets state back to original state', async () => { + function User() { + const [getUser, { isSuccess, isUninitialized, reset }, _lastInfo] = + api.endpoints.getUser.useLazyQuery() + + const handleFetchClick = async () => { + await getUser(1).unwrap() + } + + return ( +
+ + {isUninitialized + ? 'isUninitialized' + : isSuccess + ? 'isSuccess' + : 'other'} + + + +
+ ) + } + + render(, { wrapper: storeRef.wrapper }) + + await screen.findByText(/isUninitialized/i) + expect(countObjectKeys(storeRef.store.getState().api.queries)).toBe(0) + + userEvent.click(screen.getByRole('button', { name: 'Fetch User' })) + + await screen.findByText(/isSuccess/i) + expect(countObjectKeys(storeRef.store.getState().api.queries)).toBe(1) + + userEvent.click( + screen.getByRole('button', { + name: 'Reset', + }), + ) + + await screen.findByText(/isUninitialized/i) + expect(countObjectKeys(storeRef.store.getState().api.queries)).toBe(0) + }) }) describe('useMutation', () => { diff --git a/packages/toolkit/src/query/tests/unionTypes.test-d.ts b/packages/toolkit/src/query/tests/unionTypes.test-d.ts index 6426556428..819a150d56 100644 --- a/packages/toolkit/src/query/tests/unionTypes.test-d.ts +++ b/packages/toolkit/src/query/tests/unionTypes.test-d.ts @@ -9,6 +9,7 @@ import type { TypedUseQueryStateResult, TypedUseQuerySubscriptionResult, TypedLazyQueryTrigger, + TypedUseLazyQueryStateResult, TypedUseLazyQuery, TypedUseLazyQuerySubscription, TypedUseMutation, @@ -820,7 +821,7 @@ describe('"Typed" helper types', () => { >().toMatchTypeOf(trigger) expectTypeOf< - TypedUseQueryHookResult + TypedUseLazyQueryStateResult >().toMatchTypeOf(result) }) @@ -834,7 +835,12 @@ describe('"Typed" helper types', () => { >().toMatchTypeOf(trigger) expectTypeOf< - TypedUseQueryHookResult + TypedUseLazyQueryStateResult< + string, + void, + typeof baseQuery, + { x: boolean } + > >().toMatchTypeOf(result) })