如何在Ionic React中实时更新React Hook Form的isDirty值
问题:Ionic React中react-hook-form的isDirty未实时更新
我用Ionic React开发了一个应用,包含两个展示组件(其中一个是模态框组件MyModal),还有两个自定义Hook:useModalHandler负责模态框的打开关闭,useModalForm处理表单逻辑。需求是用户输入表单字段时,实时更新isDirty的值,这样在用户未保存就关闭模态框时,能弹出UnsavedChangesAlert提示数据丢失。但实际用户输入时isDirty并没有即时更新,请问问题出在哪里?
相关组件代码
Management 组件
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonIcon, IonLabel, } from "@ionic/react"; import { add } from "ionicons/icons"; import MyModal from "./Modal/MyModal"; import useModalForm from "../../../Hooks/useModalForm"; import useModalHandler from "../../../Hooks/useModalHandler"; const Management: React.FC<any> = () => { const { handleSubmit, register, onSubmit, reset, formState: { errors, isDirty }, } = useModalForm(); const { isOpen, showAlert, openModal, closeModal, handleCancel, handleConfirm, } = useModalHandler(); return ( <IonPage> <IonHeader> <IonToolbar> <IonTitle>Management</IonTitle> </IonToolbar> </IonHeader> <IonContent> <IonButton expand="block" onClick={openModal}> <IonIcon icon={add} /> <IonLabel>Add Product</IonLabel> </IonButton> <MyModal isOpen={isOpen} closeModal={closeModal} register={register} handleSubmit={handleSubmit} onSubmit={onSubmit} errors={errors} isDirty={isDirty} reset={reset} showAlert={showAlert} handleCancel={handleCancel} handleConfirm={handleConfirm} /> </IonContent> </IonPage> ); }; export default Management;
MyModal 组件
import React from "react"; import { IonModal, IonHeader, IonToolbar, IonButton, IonIcon, IonContent, IonItem, IonLabel, IonInput, IonCard, IonCardContent, } from "@ionic/react"; import UnsavedChangesAlert from "../Alert/MyAlert"; interface MyModalProps { isOpen: boolean; closeModal: (isDirty: boolean) => void; errors: any; isDirty: boolean; register: any; handleSubmit: any; onSubmit: (event: any) => void; reset: any; showAlert: boolean; handleCancel: () => void; handleConfirm: () => void; }; const MyModal: React.FC<MyModalProps> = ({ isOpen, closeModal, register, onSubmit, handleSubmit, errors, isDirty, showAlert, handleCancel, handleConfirm, }) => { return ( <IonModal isOpen={isOpen}> <IonHeader> <IonToolbar> <IonButton slot="start" onClick={() => closeModal(isDirty)}> <IonIcon slot="icon-only" name="arrow-back-outline"></IonIcon> </IonButton> <IonButton slot="end" type="submit" form="myForm"> <IonLabel>Save</IonLabel> </IonButton> </IonToolbar> </IonHeader> <IonContent> <IonCard> <IonCardContent> <form onSubmit={handleSubmit(onSubmit)} id="myForm"> <IonItem> <IonLabel position="floating">Product Name</IonLabel> <IonInput type="text" {...register("name", { required: true })} ></IonInput> </IonItem> {errors.name && <span>This field is required</span>} <IonItem> <IonLabel position="floating">Amount</IonLabel> <IonInput type="number" {...register("amount")} ></IonInput> </IonItem> </form> </IonCardContent> </IonCard> {isDirty && showAlert && ( <UnsavedChangesAlert isOpen={showAlert} onCancel={handleCancel} onConfirm={handleConfirm} /> )} </IonContent> </IonModal> ); }; export default MyModal;
useModalForm Hook
import { useForm } from "react-hook-form"; const useModalForm = () => { const { register, handleSubmit, formState: { errors, isDirty }, reset } = useForm({ defaultValues: { name: '', amount: '', }}); console.log(isDirty); const onSubmit = (data: any) => { console.log(JSON.stringify(data, null, 2)); reset(); }; return { register, handleSubmit, onSubmit, reset, formState: { errors, isDirty, } }; }; export default useModalForm;
useModalHandler Hook
import { useState } from "react"; const useModalHandler = () => { const [isOpen, setIsOpen] = useState(false); const [showAlert, setShowAlert] = useState(false); const openModal = () => { setIsOpen(true); setShowAlert(false); }; const closeModal = (isDirty: boolean) => { if (isDirty) { setShowAlert(true); } else { setIsOpen(false); } }; const handleCancel = () => { setIsOpen(false); setShowAlert(false); }; const handleConfirm = () => { setIsOpen(false); setShowAlert(false); }; return { isOpen, showAlert, openModal, closeModal, handleCancel, handleConfirm, }; }; export default useModalHandler;
UnsavedChangesAlert 组件
import { IonAlert } from "@ionic/react"; const UnsavedChangesAlert: React.FC<{ isOpen: boolean; onCancel: () => void; onConfirm: () => void; }> = ({ isOpen, onCancel, onConfirm }) => { return ( <IonAlert isOpen={isOpen} header="Unsaved Changes" message="Are you sure you want to discard unsaved changes?" buttons={[ { text: "Cancel", role: "cancel", cssClass: "secondary", handler: onCancel, }, { text: "Unsave Changes", handler: onConfirm, }, ]} /> ); }; export default UnsavedChangesAlert;
问题分析与解决方案
核心问题
- formState响应性丢失:在
useModalForm中,你将解构后的errors和isDirty重新包装成普通对象formState返回,但react-hook-form的formState是响应式代理对象,这种包装会破坏其响应性,导致组件无法感知isDirty的变化。 - 模态框打开时未重置表单:每次打开模态框时,表单状态没有重置,可能导致
isDirty保留上一次的状态,影响判断。
修复代码
1. 修改useModalForm,保留响应性
直接返回单独的errors和isDirty,避免重新包装成普通对象:
import { useForm } from "react-hook-form"; const useModalForm = () => { const { register, handleSubmit, formState: { errors, isDirty }, reset } = useForm({ defaultValues: { name: '', amount: '', } }); const onSubmit = (data: any) => { console.log(JSON.stringify(data, null, 2)); reset(); }; // 直接返回所需属性,不包装formState return { register, handleSubmit, onSubmit, reset, errors, isDirty }; }; export default useModalForm;
2. 修改Management组件,打开模态框时重置表单
确保每次打开模态框时,表单回到初始状态,isDirty重置为false:
import { IonPage, IonHeader, IonToolbar, IonTitle, IonContent, IonButton, IonIcon, IonLabel, } from "@ionic/react"; import { add } from "ionicons/icons"; import MyModal from "./Modal/MyModal"; import useModalForm from "../../../Hooks/useModalForm"; import useModalHandler from "../../../Hooks/useModalHandler"; const Management: React.FC<any> = () => { const { handleSubmit, register, onSubmit, reset, errors, isDirty, } = useModalForm(); const { isOpen, showAlert, openModal, closeModal, handleCancel, handleConfirm, } = useModalHandler(); // 打开模态框前重置表单 const handleOpenModal = () => { reset(); openModal(); }; return ( <IonPage> <IonHeader> <IonToolbar> <IonTitle>Management</IonTitle> </IonToolbar> </IonHeader> <IonContent> <IonButton expand="block" onClick={handleOpenModal}> <IonIcon icon={add} /> <IonLabel>Add Product</IonLabel> </IonButton> <MyModal isOpen={isOpen} closeModal={closeModal} register={register} handleSubmit={handleSubmit} onSubmit={onSubmit} errors={errors} isDirty={isDirty} reset={reset} showAlert={showAlert} handleCancel={handleCancel} handleConfirm={handleConfirm} /> </IonContent> </IonPage> ); }; export default Management;
3. 优化UnsavedChangesAlert的显示逻辑
原代码中{isDirty && showAlert && (...)可以简化为仅依赖showAlert,因为showAlert只有在isDirty为true时才会被设置为true:
// 在MyModal组件中替换原alert渲染代码 {showAlert && ( <UnsavedChangesAlert isOpen={showAlert} onCancel={handleCancel} onConfirm={handleConfirm} /> )}
内容的提问来源于stack exchange,提问作者Denis
相关产品推荐
相关产品推荐

