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

如何在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;

问题分析与解决方案

核心问题

  1. formState响应性丢失:在useModalForm中,你将解构后的errors和isDirty重新包装成普通对象formState返回,但react-hook-form的formState是响应式代理对象,这种包装会破坏其响应性,导致组件无法感知isDirty的变化。
  2. 模态框打开时未重置表单:每次打开模态框时,表单状态没有重置,可能导致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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.08.03 16:25:21