Skip to content

Commit d5eb220

Browse files
committed
createAsyncThunk return fulfilled/rejected action instead of re-trowing errors
1 parent fbba32d commit d5eb220

File tree

2 files changed

+39
-23
lines changed

2 files changed

+39
-23
lines changed

src/createAsyncThunk.ts

+15-16
Original file line numberDiff line numberDiff line change
@@ -101,34 +101,33 @@ export function createAsyncThunk<
101101
) => {
102102
const requestId = nanoid()
103103

104-
let result: Returned
104+
let finalAction: ReturnType<typeof fulfilled | typeof rejected>
105105
try {
106106
dispatch(pending(requestId, args))
107107

108-
result = (await payloadCreator(args, {
109-
dispatch,
110-
getState,
111-
extra,
112-
requestId
113-
} as TA)) as Returned
108+
finalAction = fulfilled(
109+
await payloadCreator(args, {
110+
dispatch,
111+
getState,
112+
extra,
113+
requestId
114+
} as TA),
115+
requestId,
116+
args
117+
)
114118
} catch (err) {
115119
const serializedError = miniSerializeError(err)
116-
dispatch(rejected(serializedError, requestId, args))
117-
// Rethrow this so the user can handle if desired
118-
throw err
120+
finalAction = rejected(serializedError, requestId, args)
119121
}
120122

121123
// We dispatch "success" _after_ the catch, to avoid having any errors
122124
// here get swallowed by the try/catch block,
123125
// per https://twitter.com/dan_abramov/status/770914221638942720
124126
// and https://redux-toolkit.js.org/tutorials/advanced-tutorial#async-error-handling-logic-in-thunks
125-
return dispatch(fulfilled(result!, requestId, args))
127+
dispatch(finalAction)
128+
return finalAction
126129
}
127130
}
128131

129-
actionCreator.pending = pending
130-
actionCreator.rejected = rejected
131-
actionCreator.fulfilled = fulfilled
132-
133-
return actionCreator
132+
return Object.assign(actionCreator, { pending, rejected, fulfilled })
134133
}
+24-7
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { createAsyncThunk, Dispatch, createReducer } from 'src'
1+
import { createAsyncThunk, Dispatch, createReducer, AnyAction } from 'src'
22
import { ThunkDispatch } from 'redux-thunk'
33

44
function expectType<T>(t: T) {
@@ -7,20 +7,37 @@ function expectType<T>(t: T) {
77
function fn() {}
88

99
// basic usage
10-
{
11-
const dispatch = fn as ThunkDispatch<any, any, any>
10+
;(async function() {
11+
const dispatch = fn as ThunkDispatch<{}, any, AnyAction>
1212

1313
const async = createAsyncThunk('test', (id: number) =>
1414
Promise.resolve(id * 2)
1515
)
16-
dispatch(async(3))
1716

1817
const reducer = createReducer({}, builder =>
1918
builder
20-
.addCase(async.pending, (_, action) => {})
19+
.addCase(async.pending, (_, action) => {
20+
expectType<ReturnType<typeof async['pending']>>(action)
21+
})
2122
.addCase(async.fulfilled, (_, action) => {
23+
expectType<ReturnType<typeof async['fulfilled']>>(action)
2224
expectType<number>(action.payload)
2325
})
24-
.addCase(async.rejected, (_, action) => {})
26+
.addCase(async.rejected, (_, action) => {
27+
expectType<ReturnType<typeof async['rejected']>>(action)
28+
expectType<Error>(action.error)
29+
})
2530
)
26-
}
31+
32+
const result = await dispatch(async(3))
33+
34+
if (async.fulfilled.match(result)) {
35+
expectType<ReturnType<typeof async['fulfilled']>>(result)
36+
// typings:expect-error
37+
expectType<ReturnType<typeof async['rejected']>>(result)
38+
} else {
39+
expectType<ReturnType<typeof async['rejected']>>(result)
40+
// typings:expect-error
41+
expectType<ReturnType<typeof async['fulfilled']>>(result)
42+
}
43+
})()

0 commit comments

Comments
 (0)