Enhancing UX: Leveraging Nextjs Router Events for Form Manipulation
Tags: webdev, nextjs, react, javascript
Published: 2023-11-18 08:55:33

## **Scenario** 💻
When manipulating form data with a **considerable number of input fields**
whether it be five, ten, or even more than twenty or thirty field and it can be quite frustrating for users. 

Imagine a scenario where a user has filled out 12 out of 20 fields, and then, unintentionally, they click on another link or navigate to a different page, resulting in the loss of all the entered data.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/t238nqmtifbjboo8mcok.png)

> "Consider a scenario where you manipulate a form, and then, when you attempt to click on a different item in the left menu navbar, a custom popup should appear to prevent you from leaving the current page."

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/fsmw631b1hyu3zvfcj72.png)

In the example below, I'll demonstrate a simple layout created using **Mantine** and **Next.js.** To address this issue, we'll implement a solution that displays a custom popup when attempting to leave the current page with unsaved form data.
```javascript
"next": "13.4.13",
"@mantine/modals": "^6.0.15",
"@mantine/next": "^6.0.15",
"@mantine/notifications": "^6.0.15",`
```

## **Manipulate** 💻
**We will use the router event to manipulate, so you might want to conduct some research on 'Router events' in Next.js.**
> You can listen to different events happening inside the Nextjs Router and subscribe to router events on any component in your application.

as mentioned in the [Next.js router documentation.
](https://nextjs.org/docs/pages/api-reference/functions/use-router)

For example, to listen to the router event routeChangeStart, open or create pages/_app.js and subscribe to the event, like so:

```javascript
import { useEffect } from 'react'
import { useRouter } from 'next/router'
 
export default function MyApp({ Component, pageProps }) {
  const router = useRouter()
 
  useEffect(() => {
    const handleRouteChange = (url, { shallow }) => {
      console.log(
        `App is changing to ${url} ${
          shallow ? 'with' : 'without'
        } shallow routing`
      )
    }
 
    router.events.on('routeChangeStart', handleRouteChange)
 
    // If the component is unmounted, unsubscribe
    // from the event with the `off` method:
    return () => {
      router.events.off('routeChangeStart', handleRouteChange)
    }
  }, [router])
 
  return <Component {...pageProps} />
}
```

Now, let's apply this technique to our source code:

```javascript
import { useEffect, useState } from 'react';
import { useRouter } from 'next/router';
import { modals } from '@mantine/notifications';

const YourComponent = () => {
  const router = useRouter();
  const [isSubmitSuccess, setIsSubmitSuccess] = useState(false);

  const handleSubmitForm = (params) => {
    // Process params and call the API
    // After success, redirect to the homepage
    setIsSubmitSuccess(true);
    router.push('/');
  };

  const exitingFunction = (nextPath) => {
    // If the submission is successful, 
    // No need to prevent the router
    if (isSubmitSuccess) {
      return;
    }

    // Display a confirmation popup before discarding changes
    modals.openContextModal({
      modal: 'confirm',
      withCloseButton: false,
      innerProps: {
        title: 'Discard changes',
        message: 'Are you sure you want to discard changes?',
        onOk: () => {
          removeListener();
          modals.closeAll();
          router.push(nextPath);
        },
      },
    });

    // Throw an error to prevent the router change
    throw 'cancelRouteChange';
  };

  const removeListener = () => {
    setIsSubmitSuccess(false);
    router.events.off('routeChangeStart', exitingFunction);
  };

  useEffect(() => {
    router.events.on('routeChangeStart', exitingFunction);
    return removeListener;
  }, [isSubmitSuccess, router]);

  return (
    <form onSubmit={handleSubmitForm}>
      {/* Your form fields go here */}
      <button type="submit">Submit</button>
    </form>
  );
};

export default YourComponent;
```

## **The Route Cancellation Mechanism**💻

The line `throw 'cancelRouteChange'; `is used as a mechanism to prevent the router change. In the context of the provided code, this line is employed when the user attempts to leave the current page without submitting the form. 

By throwing an error with the message 'cancelRouteChange', the application signals that the route change should be canceled.

This approach aligns with how Next.js handles route changes. When an error is thrown during a route change, Next.js interprets it as a signal to stop the navigation process. 

In conclusion, we've explored the powerful combination of Next.js Router Events and custom popups to create a more secure and user-friendly form interaction experience. By intercepting page transitions and providing users with the option to confirm or cancel, we've added an extra layer of protection against inadvertent data loss.

Thank you for joining me on this exploration! This marks my first attempt at sharing knowledge, and I look forward to many more opportunities to learn and grow together.🙋‍♂️ 
Happy coding!💻