Reputation: 49
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
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