Denis
Denis

Reputation: 49

How to properly update isDirty values in react hook form based on Ionic React

I have an application on Ionic react, there are two presentation components, one of them draws the other, the second modal, there are two hooks, one is responsible for opening and closing the modal, the second for form processing logic, I need to check if the form is dirty when the field is entered, I need the value isDirty to be updated immediately, because I want to pass its changed value from useModalForm through Management and MyModal to the useModalHandler hook as an argument to the "closeModal" function, so that when the user starts entering a value in the field, isDirty changes to true and if I close the form without saving, myAlert should be called which warns about data loss. But in fact, isDirty does not change the value at the time the user enters the field, what could be the problem?

Management component

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 component

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 component

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;

Upvotes: 0

Views: 2749

Answers (1)

saguirrews
saguirrews

Reputation: 330

When using useForm, you can specify a mode:

mode: onChange | onBlur | onSubmit | onTouched | all

You can use the onChange or onTouched modes to achieve what you're looking for. In your useForm declaration, it would look like this:

const {
    handleSubmit, 
    register,
    onSubmit,
    reset,
    formState: { errors, isDirty },
  } = useModalForm({ mode: 'onTouched' });

Upvotes: 1

Related Questions