注册表单状态清理-Bug修复.md 9.0 KB

注册表单状态清理 - Bug分析与修复

Bug 描述

问题场景

  1. 第一次填写完整的患者信息并注册成功
  2. 表单UI清空,但内部状态未清空
  3. 第二次不填写任何信息,直接选择体位后点击注册
  4. ❌ Bug:仍然可以注册成功(应该失败)

根本原因分析

涉及的领域模型概念

这个bug涉及到两个独立的状态管理域

1. Ant Design Form State(组件级状态)

const [form] = Form.useForm();
// 内部维护表单字段值
// 生命周期:从组件创建到手动重置
// 清理方式:form.resetFields()

特性

  • 状态持久化:即使UI不显示,内部store仍然保留数据
  • 独立管理:不受Redux状态影响
  • 需要显式清理:必须调用form.resetFields()

2. Redux formSlice State(全局状态)

interface FormState {
  formData: Record<string, any>;
}
// 存储表单数据的副本
// 用于跨组件共享

特性

  • 全局可访问
  • 需要通过action更新
  • 需要显式清理action

因果链条(清晰明确)

第一次注册成功
  ↓
触发事件:registerWork API 返回 code === '0x000000'
  ↓
当前行为(Bug原因):
  - ✅ Redux viewSelection.selectedViews 被清空
  - ❌ Redux formSlice.formData 未清空
  - ❌ Ant Design Form 内部状态未清空
  ↓
状态不一致:
  - UI层:表单显示为空(用户视角)
  - 数据层:form.getFieldsValue() 返回旧数据(系统视角)
  ↓
第二次注册
  ↓
用户操作:只选择体位,不填表单
  ↓
点击"注册"按钮
  ↓
handleRegister() 执行:
  1. let values = form.getFieldsValue();
     // ❌ 返回第一次的完整数据(用户不知道)
  2. validateResult = schema.safeParse(values);
     // ✅ 验证通过(因为values有完整数据)
  3. 调用 registerWork(registerInfo);
     // ❌ 使用旧数据注册成功
  ↓
Bug结果:用户以为表单是空的,实际用旧数据注册成功

修复方案

方案概述

在两个关键时机清理双重状态

  1. 清理时机1:注册成功后(最重要)
  2. 清理时机3:组件卸载时(防御性)

实现细节

修改1:formSlice 添加清理action

文件src/states/patient/register/formSlice.ts

const formSlice = createSlice({
  name: 'form',
  initialState,
  reducers: {
    setFormData: (state, action) => {
      state.formData = action.payload;
    },
    // ✅ 新增:清空表单数据
    clearFormData: (state) => {
      state.formData = {};
      console.log('Redux formSlice: 表单数据已清空');
    },
  },
});

export const { setFormData, clearFormData } = formSlice.actions;

领域概念

  • Action:clearFormData - 表达"清空表单"的业务意图
  • Reducer:将状态重置为初始值 {}
  • 日志:记录状态变化,便于调试

修改2:注册成功后清理(清理时机1)

文件src/pages/patient/Register.tsx

const response = await registerWork(registerInfo);
if (response.code !== '0x000000') {
  message.error(`注册失败: ${response.description}`);
  return { success: false, views: [] };
}

// ✅ 注册成功后的三重清理
// 1. 清理 Redux formSlice
dispatch(clearFormData());

// 2. 清理 Ant Design Form
form.resetFields();

// 3. selectedViews 已在 viewSelectionSlice 中自动清空
console.log('注册成功,表单已清空,可以开始新的注册');

return { success: true, data: response.data, views: selectedViews };

因果关系

API返回成功
  ↓
触发清理三重奏:
  1. dispatch(clearFormData())  → Redux formSlice.formData = {}
  2. form.resetFields()         → Ant Design Form 内部状态清空
  3. selectedViews自动清空      → 已在viewSelectionSlice实现
  ↓
系统恢复初始状态
  ↓
用户可以开始新的注册流程 ✅

修改3:组件卸载时清理(清理时机3)

文件src/pages/patient/Register.tsx

// 清理时机3:组件卸载时清理表单
useEffect(() => {
  return () => {
    // 组件卸载时清理
    dispatch(clearFormData());
    form.resetFields();
    console.log('注册页面已卸载,表单数据已清空');
  };
}, [dispatch, form]);

领域概念

  • React生命周期:useEffect cleanup函数
  • 防御性编程:即使用户没有完成注册就离开页面,也确保状态被清理
  • 避免数据泄漏:用户再次进入注册页面时,不会看到旧数据

修复后的完整流程

正常注册流程

用户进入注册页面
  ↓
填写患者信息
  ↓
  每次字段变化 → dispatch(setFormData(allValues))
  ↓
选择体位
  ↓
  点击体位 → dispatch(addSelectedView(view))
  ↓
点击"注册"按钮
  ↓
handleRegister():
  1. 获取表单值:form.getFieldsValue()
  2. 验证表单:schema.safeParse(values)
  3. 验证通过 → 调用API:registerWork(registerInfo)
  ↓
API返回成功
  ↓
【三重清理】
  1. dispatch(clearFormData())
     → formSlice.formData = {}
  2. form.resetFields()
     → Form内部状态清空
  3. selectedViews自动清空
     → viewSelection.selectedViews = []
  ↓
显示成功消息
  ↓
用户可以开始新的注册 ✅

用户离开页面流程

用户填写部分信息
  ↓
未完成注册,切换到其他页面
  ↓
RegisterPage组件卸载
  ↓
useEffect cleanup函数执行
  ↓
【清理】
  1. dispatch(clearFormData())
  2. form.resetFields()
  ↓
用户再次进入注册页面
  ↓
看到的是干净的空表单 ✅

状态管理架构图

修复前(有Bug)

┌─────────────────────────────────────────────┐
│          注册成功后的状态                      │
├─────────────────────────────────────────────┤
│ Redux viewSelection.selectedViews: []  ✅    │
│ Redux formSlice.formData: {...旧数据}  ❌    │
│ Ant Design Form State: {...旧数据}     ❌    │
└─────────────────────────────────────────────┘
         ↓
   状态不一致!
         ↓
   第二次注册时Bug

修复后(正常)

┌─────────────────────────────────────────────┐
│          注册成功后的状态                      │
├─────────────────────────────────────────────┤
│ Redux viewSelection.selectedViews: []  ✅    │
│ Redux formSlice.formData: {}           ✅    │
│ Ant Design Form State: {}              ✅    │
└─────────────────────────────────────────────┘
         ↓
   状态一致!
         ↓
   系统恢复初始状态

关键概念总结

1. 双重状态管理

  • Form State(组件级):负责UI交互
  • Redux State(全局级):负责跨组件共享

2. 状态同步

  • 数据流入:onValuesChangedispatch(setFormData)
  • 数据清理:必须同时清理两个状态

3. 清理时机

  • 时机1:业务成功后(注册成功)
  • 时机3:组件卸载时(防御性)

4. 因果关系

用户操作 → 状态变化 → 副作用处理 → UI更新

测试验证要点

手动测试

  1. 测试场景1:连续注册两次

    • 第一次:填写完整信息 → 注册成功
    • 验证:表单已清空(UI和数据层都清空)
    • 第二次:不填表单 → 点击注册
    • 预期:提示"必填项未填写"
  2. 测试场景2:填写后离开页面

    • 填写部分信息
    • 切换到其他页面
    • 再次返回注册页面
    • 预期:看到空表单

验证点

  • 注册成功后,form.getFieldsValue() 返回空对象
  • 注册成功后,Redux formSlice.formData 为空对象
  • 注册成功后,Redux selectedViews 为空数组
  • 组件卸载后再进入,表单显示为空
  • 控制台日志正确显示清理信息

相关文件清单

已修改文件

  1. src/states/patient/register/formSlice.ts

    • 添加 clearFormData action
  2. src/pages/patient/Register.tsx

    • 导入 clearFormData, useEffect
    • 实现清理时机1:注册成功后
    • 实现清理时机3:组件卸载时

相关参考文件

  • src/states/patient/viewSelection/index.ts(selectedViews的清理)
  • src/hooks/useRegisterState.ts(状态获取)
  • src/validation/patient/registerSchema.ts(验证逻辑)

更新记录

日期 修改人 修改内容
2025/10/8 - Bug分析、修复实现、文档创建