使用 Cursor 修复 Ant Design Pro Components 的 issues


思否
UI组件库 AI HTTP React TypeScript

转载自 SegmentFault

Issue: 🐛 ModalFrom在同时设置了formRef、request和destroyOnClose时,加载过程中关闭ModalForm会报错 #8858 PR: https://github.com/ant-design/pro-components/pull/9272

最近有个小伙伴在做稳定性建设,整理了一下现有的线上报错,其中有一个 form.resetFields is not a function 问题分配给我了。 问题之前就我就有发现,后来因为业务比较急,加上问题是组件库引入的就没管,既然给到了我,那我就排查一下,提供了最小复现 Demo证明确实和我们项目的骚代码无关

因为用的也是开源库嘛,我就想着看看是不是也有大兄弟发现了这个问题,果然查到了,然后一看 (opened on Nov 13, 2024) 。

得了,自己修复吧,然后就有了今天这篇记录

代码大师提示词

我们都知道,如果能给 AI 限定一个范围,给定一个明确身份,做明确的一件事情,最好是不管几个 AI或者几个人看到这段文字都能用同一种方式完成这件事,那么就是一个成功的提示词。

你是一位经验丰富的前端代码大师,擅长 React、TypeScript、JavaScript、CSS 前端相关技术。你的任务是帮助我修复开源项目中的 issues,并以最佳实践提交 Pull Request。

这是本次要修复的 issues:https://github.com/ant-design/pro-components/issues/8858
### 角色要求
- 遵循代码整洁之道,风格统一,符合项目规范(ESLint、Prettier、TypeScript 配置等)。
- 提供最小化且精准的改动,避免引入不必要的依赖。
- 在修复问题时,分析 issue 的背景,定位根因,提出多种解决思路,并选出最优解。
- 保持兼容性,避免破坏性修改。
- 严格按照 GitHub 开源协作流程:分析 issue → 编写/更新测试用例 → 本地验证 → 修改代码 → 本地验证 → 编写清晰 commit message → 提交 PR。
- 编写易读的 PR 描述,包括:
  1. 问题背景和复现步骤
  2. 解决方案及原因
  3. 修改点总结
  4. 测试结果说明(截图或日志)

### 输出格式要求
- 提供完整的代码 diff 或修复文件。
- 如果需要新建/修改测试,给出 Jest/Vitest 或 Cypress 测试示例。
- 给出 Git commit message(遵循 Conventional Commits)。
- 给出一份标准化 PR 描述(使用中英双语)。

### 注意事项
- 严格遵守仓库的 CONTRIBUTING.md 和 CODE_OF_CONDUCT。
- 如果 issue 涉及性能、可访问性、国际化,需特别指出优化点。
- 任何改动需确保通过已有测试,并尽可能补充新的测试覆盖场景。

代码大师的工作成果

ModalForm 在关闭时安全访问 form.resetFields

说实话,第一个修改我就不喜欢,我要将其修改为下面这种

DrawerForm 也有同样的问题

修改方法同上,这里我非常满意,AI 替我发现另一个相同的错误,不用再次提示

测试用例

学习 AI 是如何完成这件事情

我先查看 GitHub issue 详情来确定复现步骤与根因范围,然后在代码库中并行检索相关实现位置,准备定位问题并编写修复。

您好,针对您提供的 Issue #8858,以下是修复方案及相关信息:
## 问题背景和复现步骤
### 问题描述
在 ModalForm 组件中,同时设置 formRef、request 和 destroyOnClose 属性时,若在数据加载过程中关闭 ModalForm,会抛出错误:“form.resetFields is not a function”。
  • 配置 ModalForm,同时设置 formRef、request 和 destroyOnClose 属性。
  • 在表单数据加载过程中,关闭 ModalForm。

观察控制台,出现错误:“form.resetFields is not a function”。

解决方案及原因

根本原因:

在 ModalForm 组件中,request 属性用于异步加载表单数据,而 destroyOnClose 属性在关闭时销毁表单。当表单数据尚未加载完成时,关闭表单会导致 form 实例被销毁,从而在调用 resetFields 方法时抛出错误。

解决方案:

在 ModalForm 组件中,添加对 form 实例的存在性检查,确保在调用 resetFields 方法前,form 实例已被正确初始化。

  • 我将先在代码库中定位 ModalForm 的实现与关闭时的重置逻辑,特别是与 request、destroyOnClose 交互处,找出调用 resetFields 的位置以确定最小修复点,然后并行检索相关演示与现有测试。
  • 我将打开并阅读 GitHub issue #8858 的具体内容与讨论,以确认复现细节和真实报错信息,然后并行检索代码库中 ModalForm 与 DrawerForm 的关闭销毁逻辑与 destroyOnClose/destroyOnHidden 的处理差异,定位根因和最小修复点。
  • 我先标记当前任务为“分析并定位 ModalForm 关闭与 resetFields 调用逻辑”,然后并行读取 ModalForm、DrawerForm、BaseForm 相关实现与已有测试,确认 destroyOnHidden 与 resetFields 的触发点,并查找是否存在 destroyOnClose 与 destroyOnHidden 的混用问题。随后我会根据结果决定最小修复方案。
  • 我将先更新 todo,将“分析并定位 ModalForm 关闭与 resetFields 调用逻辑”标记完成,并开始“实现最小修复:在关闭时安全访问 form.resetFields”。随后我会直接编辑 ModalForm 和 DrawerForm 的重置逻辑,加上函数存在性判断。完成后再补测试并运行。
  • 我将修改 ModalForm 和 DrawerForm 的 resetFields 调用,加上 typeof form.resetFields === ‘function’ 判断,并新增一个针对 request+destroyOnHidden 的关闭场景测试用例。随后运行测试验证。
  • 我将对 ModalForm 与 DrawerForm 中的 resetFields 调用增加方法存在性判断,并新增一个模拟 request 加载中关闭的测试用例,随后运行对应测试。
  • 我刚把实现与测试都完成并通过了全量用例。现在我会将“撰写 Conventional Commit 与 PR 描述”标记完成,然后给出代码引用、提交信息与 PR 描述。

AI 输出结果

简要对齐

  • 你的 Issue 链接指出:ModalForm 同时使用 formRefrequest 与关闭销毁时(destroyOnHidden/destroyOnClose),在加载中关闭会报错 “form.resetFields is not a function”。
  • 代码走查显示,项目内部统一使用 destroyOnHidden(非 antd 原生的 destroyOnClose)控制关闭时销毁。风险点在于关闭时 form 实例可能尚未就绪或已被销毁,从而直接调用 resetFields 报错。
  • 目标:最小化修复,保证兼容、不引入依赖、通过全部测试。

最小修复点(代码 diff)

  • ModalFormDrawerForm 的关闭重置逻辑里,为 resetFields 增加安全存在性检查,避免在 form 尚未初始化或已销毁时调用。

代码引用:

  const resetFields = useCallback(() => {
    const form = rest.form ?? rest.formRef?.current ?? formRef.current;
    // 重置表单
    if (modalProps?.destroyOnHidden && form && typeof (form as any).resetFields === 'function') {
      (form as any).resetFields();
    }
  }, [modalProps?.destroyOnHidden, rest.form, rest.formRef]);
  const resetFields = useCallback(() => {
    const form = rest.formRef?.current ?? rest.form ?? formRef.current;
    // 重置表单
    if (drawerProps?.destroyOnHidden && form && typeof (form as any).resetFields === 'function') {
      (form as any).resetFields();
    }
  }, [drawerProps?.destroyOnHidden, rest.form, rest.formRef]);

测试补充

新增用例:在 ModalForm 使用 requestdestroyOnHidden: true 时,打开后立即关闭(加载中),不应报错。

代码引用:

  it('📦 ModalForm close during request loading with destroyOnHidden should not throw', async () => {
    const wrapper = render(
      <ModalForm
        width={600}
        modalProps={{ destroyOnHidden: true }}
        request={async () => {
          // Simulate slow request
          return new Promise((resolve) => setTimeout(() => resolve({ name: 'demo' }), 200));
        }}
        trigger={<Button id="new">新建</Button>}
      >
        <ProFormText name="name" />
      </ModalForm>,
    );

    await act(async () => {
      const triggerButton = wrapper.getByText('新 建');
      fireEvent.click(triggerButton);
    });

    // Close before request resolves (cancel button may not render while loading), click close icon
    await act(async () => {
      const closeButton = document.querySelector('button.ant-modal-close');
      if (closeButton) {
        fireEvent.click(closeButton);
      }
    });

    // Wait a bit for async to flush
    await waitForWaitTime(300);
  });
  • 本地验证:已运行完整 pnpm test,全部通过(71 files, 1563 passed, 1 skipped)。新增用例单测也通过。

解决思路对比与选择

  • 方案A:在关闭时调用 resetFields 前做方法存在性检查(已选)。最小改动、兼容性好。
  • 方案B:在 BaseForm 请求完成后再触发重置。副作用大,改变行为时机,风险高。
  • 方案C:关闭时取消/中断 request。涉及 useFetchData 取消逻辑,改动面广。
  • 结论:选择方案A,最小化、稳妥。

提交信息(Conventional Commits)

fix(form): prevent error when closing ModalForm/DrawerForm during request with destroyOnHidden

- Guard resetFields call to ensure form instance and method exist
- Cover scenario with a new test: close ModalForm while request is loading
- Keep backward compatibility; no breaking changes

Closes #8858

标准化 PR 描述(中英双语)

问题背景与复现步骤: 使用 ModalForm,同时配置 formRefrequestmodalProps={{ destroyOnHidden: true }}

  • 打开弹窗后,接口加载尚未返回时立即关闭。
  • 控制台出现错误 “form.resetFields is not a function”。

解决方案及原因: 在关闭时重置表单逻辑中,增加 resetFields 方法存在性检查,避免在 form 尚未初始化或已被销毁时调用导致报错。该方案最小化修改、保持既有行为。

修改点总结: src/form/layouts/ModalForm/index.tsx:关闭重置逻辑加方法存在性判断。

  • src/form/layouts/DrawerForm/index.tsx:同上。
  • 新增测试 tests/form/modalForm.test.tsx:覆盖“加载中关闭不报错”的场景。

测试结果: 新增用例通过。

  • 全量测试通过:71 files,1563 passed,1 skipped。

English:

  • Background & Repro:
  1. With ModalForm, configure formRef, request, and modalProps={{ destroyOnHidden: true }}.
  2. Open the modal and close it before the async request resolves.
  3. An error is thrown: “form.resetFields is not a function”.

Solution & Rationale: Add a guard before calling resetFields to ensure the form instance exists and the method is available. This avoids calling it during loading/unmount phases. Minimal and non-breaking change.

Changes: Add safe guard in ModalForm and DrawerForm reset logic.

  • Add a test to cover closing during request loading.

Test Results: New test passes.

  • Full suite passes (71 files, 1563 passed, 1 skipped).

注意点

  • 未引入第三方依赖。
  • 兼容性良好,不改变 API。
  • 与项目 destroyOnHidden 语义一致,不混用 destroyOnClose
  • 保持原行为:仅在 destroyOnHidden 为真时重置表单。

原文:SegmentFault

评论

评论功能即将开放。你也可以通过 邮件GitHub Issue 反馈。