Subrato Pattanaik
Subrato Pattanaik

Reputation: 6049

Alert user before leaving page after inputting some data (Specially in ReactJS)

I am creating a page in which the user needs to enter the details to add the product to the product list. If the user accidentally moves out of that page after inputting some data in the form, confirmation should be mandatory from the user before moving out. But I am struggling with this requirement. I am using the react-hook-form for storing the data in the JSON server.

I am using a state leave, if it true then alert the user before moving out else nothing.

const [leave, setLeave] = useState(false);

Now, I don't know where and how to use this state for displaying the alert box before leaving. Here my form which will render.

render (
<Form onSubmit={handleSubmit(onSubmit)}>
   <Form.Row>
       <Form.Group as={Col} className="mr-5">
       <Form.Label column>Product Name</Form.Label>
       <Form.Control
            name="productName"
            placeholder="Enter product Name"
            required
            ref={register}
            />
       </Form.Group>
   </Form.Row>
   <Button variant="success" type="submit" className="text-white mt-5">
            Add New Product
   </Button>
</Form>

<Prompt when={Leave} message="Are you sure you want to leave ?" /> {/*Here I have promted*/}
);

For simplicity, I have given only one input field. Now function definition of onSubmit is given below.

const onSubmit = (formData) => {
    setLeave(true);   //here I am setting this true
    axios.post("http://localhost:4000/products", formData);
    navigation.push({
        pathname: "/productList",
        state: { added: "pass" },
      });
};

This will work when I submitting the form but I want to prompt when the user clicks on the back button or any navigation link.

Upvotes: 2

Views: 11343

Answers (3)

Kodali444
Kodali444

Reputation: 1443

In functional component of reactjs I used below thing. this worked for me. In my case when leaving current page I want to remove some store data. In that case i used below thing.

useEffect(() => {
  return () => {
       // you can add your functionality here  
   };
 }, []);

Upvotes: 2

Subrato Pattanaik
Subrato Pattanaik

Reputation: 6049

I have updated the state isDirty or leave to true when there is a change in the input field of the form as shown below

render (
<Form onSubmit={handleSubmit(onSubmit)}>
   <Form.Row>
       <Form.Group as={Col} className="mr-5">
       <Form.Label column>Product Name</Form.Label>
       <Form.Control
            name="productName"
            placeholder="Enter product Name"
            required
            onChange={() => setIsDirty(true)} {/* I have updated here*/}
            ref={register}
            />
       </Form.Group>
   </Form.Row>
   <Button variant="success" type="submit" className="text-white mt-5">
            Add New Product
   </Button>
</Form>

<Prompt when={isDirty} message="Are you sure you want to leave ?" /> 
);

Now inside the onSubmit function, I have updated the isDirty state to false before navigating to the destination.

const onSubmit = (formData) => {
    axios.post("http://localhost:4000/products", formData);
    setIsDirty(false);   //here I am setting this true
    navigation.push({
        pathname: "/productList",
        state: { added: "pass" },
      });
};

I'd like to share an important point here that whenever there is a change in input at any point then only isDirty state is true and during submission of the form (all input fields are filled) the state change to false so that it can navigate to another URL.

Upvotes: 2

Lynden Noye
Lynden Noye

Reputation: 1011

when must be true when you want to interrupt navigation.

I would rename your variable to something like isDirty, initialised as false. I've found since I started naming my boolean flags like this my mind has to do a little less work to understand what it might be being used for.

Flip it to true when the first change is made to the form values. Could be simply a useEffect with formData as a dependency? Check its not already flipped to prevent unnecessary renders.

Flip it back to false when you get a 200 from your submission to allow the subsequent navigation.

I don't think React will re-render before the Prompt gets fired in your current onSubmit, so you may need to figure out a way to navigate after verifying the submission.

Another flag like didSubmit would allow you a code path to render a <Redirect> instead of calling navigation.push(), if that suits.

But otherwise, using your current setup, can can allow navigation when you land on a fresh form (your user hasn't committed any effort yet), navigation will be interrupted if the form is 'dirty', and navigation will happen automatically on the next render after setting didSubmit.

const [isDirty, setIsDirty] = React.useState(false)
const [didSubmit, setDidSubmit] = React.useState(false)
const onSubmit = async (formData) => {
  try {
    await axios.post('url', formData)
    setIsDirty(false)
    setDidSubmit(true)
  } catch (error) {
    // handle your errors
  }
}
React.useEffect(() => {
  if(!isDirty) setIsDirty(true)
}, [formData])
React.useEffect(() => {
  if(didSubmit) navigation.push({ ... })
}, [didSubmit]

return (
  // the form...
  <Prompt when={isDirty} {...} />
)

Upvotes: 1

Related Questions