Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

add reset method to useMutation hook #1476

Merged
merged 6 commits into from
Oct 1, 2021
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 11 additions & 10 deletions docs/rtk-query/api/created-api/hooks.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -316,9 +316,9 @@ type UseMutationStateOptions = {
selectFromResult?: (result: UseMutationStateDefaultResult) => any
}

type UseMutationTrigger<T> = (
arg: any
) => Promise<{ data: T } | { error: BaseQueryError | SerializedError }> & {
type UseMutationTrigger<T> = (arg: any) => Promise<
{ data: T } | { error: BaseQueryError | SerializedError }
> & {
requestId: string // A string generated by RTK Query
abort: () => void // A method to cancel the mutation promise
unwrap: () => Promise<T> // A method to unwrap the mutation call and provide the raw response/error
Expand Down Expand Up @@ -360,7 +360,10 @@ selectFromResult: () => ({})

- **Returns**: A tuple containing:
- `trigger`: A function that triggers an update to the data based on the provided argument. The trigger function returns a promise with the properties shown above that may be used to handle the behavior of the promise
- `mutationState`: A query status object containing the current loading state and metadata about the request, or the values returned by the `selectFromResult` option where applicable
- `mutationState`: A query status object containing the current loading state and metadata about the request, or the values returned by the `selectFromResult` option where applicable.
Additionally, this object will contain
- a `reset` method to reset the hook back to it's original state and remove the current result from the cache
phryneas marked this conversation as resolved.
Show resolved Hide resolved
- an `originalArgs` property that contain the argument passed to the last call of the `trigger` function.
phryneas marked this conversation as resolved.
Show resolved Hide resolved

#### Description

Expand Down Expand Up @@ -459,9 +462,8 @@ type UseQuerySubscriptionResult = {
## `useLazyQuery`

```ts title="Accessing a useLazyQuery hook" no-transpile
const [trigger, result, lastPromiseInfo] = api.endpoints.getPosts.useLazyQuery(
options
)
const [trigger, result, lastPromiseInfo] =
api.endpoints.getPosts.useLazyQuery(options)
// or
const [trigger, result, lastPromiseInfo] = api.useLazyGetPostsQuery(options)
```
Expand Down Expand Up @@ -520,9 +522,8 @@ type UseLazyQueryLastPromiseInfo = {
## `useLazyQuerySubscription`

```ts title="Accessing a useLazyQuerySubscription hook" no-transpile
const [trigger, lastArg] = api.endpoints.getPosts.useLazyQuerySubscription(
options
)
const [trigger, lastArg] =
api.endpoints.getPosts.useLazyQuerySubscription(options)
```

#### Signature
Expand Down
39 changes: 17 additions & 22 deletions packages/toolkit/src/query/react/buildHooks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -377,6 +377,11 @@ export type UseMutationStateResult<
R
> = NoInfer<R> & {
originalArgs?: QueryArgFrom<D>
/**
* Resets the hook state to it's initial `uninitialized` state.
* This will also remove the last result from the cache.
*/
reset: () => void
}

/**
Expand Down Expand Up @@ -717,47 +722,37 @@ export function buildHooks<Definitions extends EndpointDefinitions>({
Definitions
>
const dispatch = useDispatch<ThunkDispatch<any, any, AnyAction>>()
const [requestId, setRequestId] = useState<string>()

const promiseRef = useRef<MutationActionCreatorResult<any>>()
const [promise, setPromise] = useState<MutationActionCreatorResult<any>>()

useEffect(() => {
return () => {
promiseRef.current?.unsubscribe()
promiseRef.current = undefined
}
}, [])
useEffect(() => () => promise?.unsubscribe(), [promise])

const triggerMutation = useCallback(
function (arg) {
let promise: MutationActionCreatorResult<any>
batch(() => {
promiseRef?.current?.unsubscribe()
promise = dispatch(initiate(arg))
promiseRef.current = promise
setRequestId(promise.requestId)
})
return promise!
const promise = dispatch(initiate(arg))
setPromise(promise)
return promise
},
[dispatch, initiate]
)

const mutationSelector = useMemo(
() =>
createSelector([select(requestId || skipToken)], (subState) =>
selectFromResult(subState)
createSelector(
[select(promise?.requestId || skipToken)],
selectFromResult
),
[select, requestId, selectFromResult]
[select, promise, selectFromResult]
)

const currentState = useSelector(mutationSelector, shallowEqual)
const originalArgs = promiseRef.current?.arg.originalArgs
const originalArgs = promise?.arg.originalArgs
const finalState = useMemo(
() => ({
...currentState,
originalArgs,
reset: promise?.unsubscribe,
Shrugsy marked this conversation as resolved.
Show resolved Hide resolved
}),
[currentState, originalArgs]
[currentState, originalArgs, promise]
)

return useMemo(
Expand Down
41 changes: 40 additions & 1 deletion packages/toolkit/src/query/tests/buildHooks.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -869,6 +869,45 @@ describe('hooks tests', () => {
expect(screen.queryByText(/Successfully updated user/i)).toBeNull()
screen.getByText('Request was aborted')
})

test('`reset` sets state back to original state', async () => {
function User() {
const [updateUser, result] = api.endpoints.updateUser.useMutation()
return (
<>
<span>
{result.isUninitialized
? 'isUninitialized'
: result.isSuccess
? 'isSuccess'
: 'other'}
</span>
<button onClick={() => updateUser({ name: 'Yay' })}>trigger</button>
<button onClick={result.reset}>reset</button>
</>
)
}
render(<User />, { wrapper: storeRef.wrapper })

await screen.findByText(/isUninitialized/i)
expect(Object.keys(storeRef.store.getState().api.mutations).length).toBe(
0
)

userEvent.click(screen.getByRole('button', { name: 'trigger' }))

await screen.findByText(/isSuccess/i)
expect(Object.keys(storeRef.store.getState().api.mutations).length).toBe(
1
)

userEvent.click(screen.getByRole('button', { name: 'reset' }))

await screen.findByText(/isUninitialized/i)
expect(Object.keys(storeRef.store.getState().api.mutations).length).toBe(
0
)
})
})

describe('usePrefetch', () => {
Expand Down Expand Up @@ -1843,8 +1882,8 @@ describe('hooks with createApi defaults set', () => {
api.internalActions.middlewareRegistered.match,
increment.matchPending,
increment.matchFulfilled,
api.internalActions.unsubscribeMutationResult.match,
increment.matchPending,
api.internalActions.unsubscribeMutationResult.match,
increment.matchFulfilled
)
})
Expand Down
1 change: 1 addition & 0 deletions packages/toolkit/src/query/tests/unionTypes.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -483,6 +483,7 @@ describe.skip('TS only tests', () => {
isLoading: true,
isSuccess: false,
isError: false,
reset: () => {},
})(result)
})

Expand Down