Skip to content

Commit 4bb0c5c

Browse files
msutkowskiphryneasmarkerikson
authored
Override unwrap behavior for buildInitiateQuery, update tests (#1786)
* Override unwrap behavior for buildInitiateQuery, update tests * Update TS test matrix to cover 4.4 and 4.5 * Fix type issue in tests * Update packages/toolkit/src/query/tests/buildHooks.test.tsx * Remove intermediate variable for aggregatePromise * Update packages/toolkit/src/query/core/buildInitiate.ts Co-authored-by: Lenz Weber <mail@lenzw.de> Co-authored-by: Mark Erikson <mark@isquaredsoftware.com>
1 parent b92aad9 commit 4bb0c5c

File tree

4 files changed

+127
-4
lines changed

4 files changed

+127
-4
lines changed

.github/workflows/tests.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -79,7 +79,7 @@ jobs:
7979
fail-fast: false
8080
matrix:
8181
node: ['14.x']
82-
ts: ['3.9', '4.0', '4.1', '4.2', '4.3', 'next']
82+
ts: ['3.9', '4.0', '4.1', '4.2', '4.3', , '4.4', '4.5', 'next']
8383
steps:
8484
- name: Checkout repo
8585
uses: actions/checkout@v2

packages/toolkit/src/query/core/buildInitiate.ts

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -276,7 +276,9 @@ Features like automatic cache collection, automatic refetching etc. will not be
276276
})
277277
const thunkResult = dispatch(thunk)
278278
middlewareWarning(getState)
279-
const { requestId, abort, unwrap } = thunkResult
279+
280+
const { requestId, abort } = thunkResult
281+
280282
const statePromise: QueryActionCreatorResult<any> = Object.assign(
281283
Promise.all([runningQueries[queryCacheKey], thunkResult]).then(() =>
282284
(api.endpoints[endpointName] as ApiEndpointQuery<any, any>).select(
@@ -289,7 +291,15 @@ Features like automatic cache collection, automatic refetching etc. will not be
289291
subscriptionOptions,
290292
queryCacheKey,
291293
abort,
292-
unwrap,
294+
async unwrap() {
295+
const result = await statePromise
296+
297+
if (result.isError) {
298+
throw result.error
299+
}
300+
301+
return result.data
302+
},
293303
refetch() {
294304
dispatch(
295305
queryAction(arg, { subscribe: false, forceRefetch: true })

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

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import type { AnyAction, ThunkAction, ThunkDispatch } from '@reduxjs/toolkit'
2-
import { createSelector, Selector } from '@reduxjs/toolkit'
2+
import { createSelector } from '@reduxjs/toolkit'
3+
import type { Selector } from '@reduxjs/toolkit'
34
import type { DependencyList } from 'react'
45
import {
56
useCallback,

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

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,13 @@ const api = createApi({
5454
body: { name: 'Timmy' },
5555
}),
5656
}),
57+
getUserAndForceError: build.query<{ name: string }, number>({
58+
query: () => ({
59+
body: {
60+
forceError: true,
61+
},
62+
}),
63+
}),
5764
getIncrementedAmount: build.query<any, void>({
5865
query: () => ({
5966
url: '',
@@ -915,6 +922,111 @@ describe('hooks tests', () => {
915922
expect(screen.queryByText(/An error has occurred/i)).toBeNull()
916923
expect(screen.queryByText('Request was aborted')).toBeNull()
917924
})
925+
926+
test('unwrapping the useLazyQuery trigger result does not throw on ConditionError and instead returns the aggregate error', async () => {
927+
function User() {
928+
const [getUser, { data, error }] =
929+
api.endpoints.getUserAndForceError.useLazyQuery()
930+
931+
const [unwrappedError, setUnwrappedError] = React.useState<any>()
932+
933+
const handleClick = async () => {
934+
const res = getUser(1)
935+
936+
try {
937+
await res.unwrap()
938+
} catch (error) {
939+
setUnwrappedError(error)
940+
}
941+
}
942+
943+
return (
944+
<div>
945+
<button onClick={handleClick}>Fetch User</button>
946+
<div data-testid="result">{JSON.stringify(data)}</div>
947+
<div data-testid="error">{JSON.stringify(error)}</div>
948+
<div data-testid="unwrappedError">
949+
{JSON.stringify(unwrappedError)}
950+
</div>
951+
</div>
952+
)
953+
}
954+
955+
render(<User />, { wrapper: storeRef.wrapper })
956+
957+
const fetchButton = screen.getByRole('button', { name: 'Fetch User' })
958+
fireEvent.click(fetchButton)
959+
fireEvent.click(fetchButton) // This technically dispatches a ConditionError, but we don't want to see that here. We want the real error to resolve.
960+
961+
await waitFor(() => {
962+
const errorResult = screen.getByTestId('error')?.textContent
963+
const unwrappedErrorResult =
964+
screen.getByTestId('unwrappedError')?.textContent
965+
966+
errorResult &&
967+
unwrappedErrorResult &&
968+
expect(JSON.parse(errorResult)).toMatchObject({
969+
status: 500,
970+
data: null,
971+
}) &&
972+
expect(JSON.parse(unwrappedErrorResult)).toMatchObject(
973+
JSON.parse(errorResult)
974+
)
975+
})
976+
977+
expect(screen.getByTestId('result').textContent).toBe('')
978+
})
979+
980+
test('useLazyQuery does not throw on ConditionError and instead returns the aggregate result', async () => {
981+
function User() {
982+
const [getUser, { data, error }] = api.endpoints.getUser.useLazyQuery()
983+
984+
const [unwrappedResult, setUnwrappedResult] = React.useState<
985+
undefined | { name: string }
986+
>()
987+
988+
const handleClick = async () => {
989+
const res = getUser(1)
990+
991+
const result = await res.unwrap()
992+
setUnwrappedResult(result)
993+
}
994+
995+
return (
996+
<div>
997+
<button onClick={handleClick}>Fetch User</button>
998+
<div data-testid="result">{JSON.stringify(data)}</div>
999+
<div data-testid="error">{JSON.stringify(error)}</div>
1000+
<div data-testid="unwrappedResult">
1001+
{JSON.stringify(unwrappedResult)}
1002+
</div>
1003+
</div>
1004+
)
1005+
}
1006+
1007+
render(<User />, { wrapper: storeRef.wrapper })
1008+
1009+
const fetchButton = screen.getByRole('button', { name: 'Fetch User' })
1010+
fireEvent.click(fetchButton)
1011+
fireEvent.click(fetchButton) // This technically dispatches a ConditionError, but we don't want to see that here. We want the real result to resolve and ignore the error.
1012+
1013+
await waitFor(() => {
1014+
const dataResult = screen.getByTestId('error')?.textContent
1015+
const unwrappedDataResult =
1016+
screen.getByTestId('unwrappedResult')?.textContent
1017+
1018+
dataResult &&
1019+
unwrappedDataResult &&
1020+
expect(JSON.parse(dataResult)).toMatchObject({
1021+
name: 'Timmy',
1022+
}) &&
1023+
expect(JSON.parse(unwrappedDataResult)).toMatchObject(
1024+
JSON.parse(dataResult)
1025+
)
1026+
})
1027+
1028+
expect(screen.getByTestId('error').textContent).toBe('')
1029+
})
9181030
})
9191031

9201032
describe('useMutation', () => {

0 commit comments

Comments
 (0)