【问题标题】:How to use createAsyncThunk with Typescript? How to set types for the `pending` and `rejected` payloads?如何将 createAsyncThunk 与 Typescript 一起使用?如何设置 `pending` 和 `rejected` 有效载荷的类型?
【发布时间】:2021-07-17 11:43:04
【问题描述】:

现在我有这些操作用于上传 thunk 的生命周期。

type UPLOAD_START   = PayloadAction<void>
type UPLOAD_SUCCESS = PayloadAction<{ src: string, sizeKb: number }> 
type UPLOAD_FAILURE = PayloadAction<{ error: string }>

我想将它转换为createAsyncThunk 调用,假设它会减少代码。但会吗?

https://redux-toolkit.js.org/api/createAsyncThunk 的示例来看,应该是这样的:

const uploadThumbnail = createAsyncThunk(
  'mySlice/uploadThumbnail',
  async (file: File, thunkAPI) => {
    const response = await uploadAPI.upload(file) as API_RESPONSE
    return response.data   // IS THIS THE payload FOR THE fulfilled ACTION ?
  }
)

这就是我将如何处理生命周期操作?

const usersSlice = createSlice({
  name: 'mySlice',
  initialState: // SOME INITIAL STATE,
  reducers: {
    // standard reducer logic, with auto-generated action types per reducer
  },
  extraReducers: {
    // Add reducers for additional action types here, and handle loading state as needed
    [uploadThumbnail.pending]: (state,action) => {
       // HANDLE MY UPLOAD_START ACTION
    },
    [uploadThumbnail.fulfilled]: (state, action) => {
      // HANDLE MY UPLOAD_SUCCESS ACTION
    },
    [uploadThumbnail.rejected]: (state, action) => {
      // HANDLE MY UPLOAD_FAILURE ACTION
    },
  }
})

问题

我假设 createAsyncThunk 异步处理程序的返回是 payload 用于 fulfilled 操作,对吗?

但是如何为pendingrejected 操作设置payload 类型?我应该将try-catch 块添加到createAsyncThunk 处理程序吗?

这是我应该做的关联吗?

  • pending === "UPLOAD_START"
  • fulfilled === "UPLOAD_SUCCESS"
  • rejected === "UPLOAD_FAILURE"

Obs: 从我想象的模式来看,看起来我编写的代码不会比我已经用三个单独的动作做的代码少,并在我的常规操作中处理它们减速器(而不是在 extraReducers 道具上做)。在这种情况下使用createAsyncThunk 有什么意义?

【问题讨论】:

    标签: typescript redux redux-thunk redux-toolkit


    【解决方案1】:

    您的大部分问题都可以通过查看您链接的文档页面下方的一个 TypeScript 示例来回答:

    export const updateUser = createAsyncThunk<
      User,
      { id: string } & Partial<User>,
      {
        rejectValue: ValidationErrors
      }
    >('users/update', async (userData, { rejectWithValue }) => {
      try {
        const { id, ...fields } = userData
        const response = await userAPI.updateById<UpdateUserResponse>(id, fields)
        return response.data.user
      } catch (err) {
        let error: AxiosError<ValidationErrors> = err // cast the error for access
        if (!error.response) {
          throw err
        }
        // We got validation errors, let's return those so we can reference in our component and set form errors
        return rejectWithValue(error.response.data)
      }
    })
    
    
    const usersSlice = createSlice({
      name: 'users',
      initialState,
      reducers: {},
      extraReducers: (builder) => {
        // The `builder` callback form is used here because it provides correctly typed reducers from the action creators
        builder.addCase(updateUser.fulfilled, (state, { payload }) => {
          state.entities[payload.id] = payload
        })
        builder.addCase(updateUser.rejected, (state, action) => {
          if (action.payload) {
            // Being that we passed in ValidationErrors to rejectType in `createAsyncThunk`, the payload will be available here.
            state.error = action.payload.errorMessage
          } else {
            state.error = action.error.message
          }
        })
      },
    })
    

    所以,从那里观察:

    • 使用 TypeScript 时,您应该为 extraReducers 使用 builder 样式表示法,您的所有类型都将自动为您推断。您永远不需要在 extraReducers 中手动输入任何内容。
    • thunk 的 returned 值将是“已完成”操作的 payload
    • 如果你return rejectWithResult(value),那将成为“拒绝”动作的payload
    • 如果你只是throw,那将成为“拒绝”动作的error

    其他答案:

    • “待定”是您的“UPLOAD_START”。它没有有效负载,您无法设置它。 不过,所有“待处理”/“已拒绝”/“已完成”三个都将具有 action.meta.arg,这是您传递给 thunk 调用的原始值。
    • 最后,这可能比您手写的代码少一些,并且在整个应用程序中都非常一致。此外,它还捕获了一些其他情况下看不到的错误。 你知道吗
      const manualThunk = async (arg) => {
        dispatch(pendingAction())
        try {
          const result = await foo(arg)
          dispatch(successAction(result))
        } catch (e) {
          dispatch(errorAction(e))
        }
      }
    

    实际上包含一个错误? 如果successAction 触发了重新渲染(它很可能会这样做)并且在重新渲染期间的某个地方,错误是thrown,则该错误将在此try..catch 块中捕获,并且将调度另一个errorAction。因此,您将同时获得成功和错误情况为真的 thunk。尴尬的。这可以通过将结果存储在作用域上的变量中并在 try-catch-block 之外进行调度来规避,但实际上谁会这样做呢? ;) 正是这些createAsyncThunk 为你照顾的小事让我觉得它值得。

    【讨论】:

      猜你喜欢
      • 2021-04-05
      • 2023-01-17
      • 2019-06-08
      • 1970-01-01
      • 1970-01-01
      • 2018-05-26
      • 1970-01-01
      • 2022-01-11
      • 2017-01-11
      相关资源
      最近更新 更多