You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

React函数组件如何捕获Redux Thunk异步方法抛出的错误

React函数组件捕获Redux Thunk异步错误的解决方案

问题根因

当前组件侧.catch()无法触发的核心原因是:thunk内部的.catch()回调已经捕获了接口请求抛出的Promise错误,最终thunk返回的是fulfilled(已完成)状态的Promise,错误被内部逻辑完全消化,外层调用方自然无法进入reject回调。
之前尝试在thunk错误分支直接抛错的思路是对的,单测失败本质是现有单测本身存在异步逻辑未等待、断言未执行的隐性问题,不是方案逻辑错误。


推荐方案:修正thunk错误传递逻辑,补全单测异步等待

这种方案符合异步逻辑的设计惯例,调用方可以通过原生Promise的then/catch区分成功失败状态,长期可维护性更好。

  1. 修改thunk逻辑,派发完错误action后将错误继续抛出,向外传递失败状态:
export const updateCostCenter = (data: Record<string, unknown>) => async (dispatch: Dispatch<IWorkforceState>) => {
  dispatch(requestUpdateCostCenter());

  return api('put', `${costCenterUrl}/${data.rowId}`, data)
    .then(response => {
      return dispatch(receiveUpdateCostCenter(response.data));
    })
    .catch(err => {
      dispatch(errorUpdateCostCenter(err.response?.data?.description));
      // 向外抛出错误,让调用方感知请求失败
      throw err;
    });
};
  1. 修正单元测试:现有单测没有将异步Promise返回给测试框架,框架不会等待异步逻辑执行完成就会结束用例,导致错误场景下的断言根本没有执行(属于无效单测)。给每个用例加上return返回Promise即可正常通过:
it('update cost center success', function() {
  mockAdd.mockImplementation(
    () => Promise.resolve({ data: costCenter } as any)
  );

  const expectedActions = [
    { type: WorkforceActions.REQUEST_UPDATE_COST_CENTER },
    { type: WorkforceActions.RECEIVE_UPDATE_COST_CENTER, costCenter },
  ];
  // 返回Promise,让测试框架等待异步逻辑执行完成
  return store.dispatch(updateCostCenter({ data: costCenter }) as any).then(() => {
    expect(store.getActions()).toEqual(expectedActions);
    expect(api).toHaveBeenCalled();
  });
});

it('update cost center error', function() {
  mockAdd.mockImplementation(
    () => Promise.reject({
        response: { data: { description: 'dummy-message' } },
      } as any)
  );

  const expectedActions = [
    { type: WorkforceActions.REQUEST_UPDATE_COST_CENTER },
    { type: WorkforceActions.ERROR_UPDATE_COST_CENTER, message: 'dummy-message' },
  ];
  // 错误场景thunk会返回rejected状态的Promise,刚好进入catch执行断言
  return store.dispatch(updateCostCenter({ data: costCenter }) as any).catch(() => {
    expect(store.getActions()).toEqual(expectedActions);
    expect(api).toHaveBeenCalled();
  });
});

改完之后组件侧原有catch逻辑可以正常触发,注意要在错误分支补充setSubmitting(false)逻辑,避免表单一直卡在提交状态。


兼容方案:不修改thunk状态,通过返回值判断执行结果

如果暂时不想调整thunk和现有单测,可以让thunk在不同执行结果下返回状态标识,组件侧在then回调里判断结果即可,不需要依赖Promise的catch捕获。

  1. 调整thunk返回值:
export const updateCostCenter = (data: Record<string, unknown>) => async (dispatch: Dispatch<IWorkforceState>) => {
  dispatch(requestUpdateCostCenter());

  return api('put', `${costCenterUrl}/${data.rowId}`, data)
    .then(response => {
      dispatch(receiveUpdateCostCenter(response.data));
      return { success: true };
    })
    .catch(err => {
      dispatch(errorUpdateCostCenter(err.response?.data?.description));
      return { success: false };
    });
};
  1. 组件侧调整调用逻辑:
props.updateCostCenter(valueToSubmit).then((result) => {
  setSubmitting(false);
  if (!result.success) {
    props.showToastNotification('error', 'Error', props.translate('cost_center_update_error'));
    return;
  }
  props.showToastNotification('success', 'Successful', props.translate('cost_center_successfully_updated'));
  AmplitudeService.logEvent(props.translate('edit_cost_center'));
  props.hideDialog();
  resetForm();
  if (props.loadData) {
    props.loadData();
  }
});

这种方案下thunk永远返回fulfilled状态的Promise,原有单测不需要改动即可运行,但要注意原有错误场景的单测因为没有等待异步逻辑,本身属于无效用例,后续建议补全。


内容的提问来源于stack exchange,提问作者stratdev

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.29 13:09:26