DEV Community

Vuong Nguyen for GlopGeek

Posted on

Enhancing UX: Leveraging Nextjs Router Events for Form Manipulation

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

"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

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.

"next": "13.4.13",
"@mantine/modals": "^6.0.15",
"@mantine/next": "^6.0.15",
"@mantine/notifications": "^6.0.15",`
Enter fullscreen mode Exit fullscreen mode

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.

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

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} />
}
Enter fullscreen mode Exit fullscreen mode

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

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;
Enter fullscreen mode Exit fullscreen mode

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!πŸ’»

Top comments (1)

Collapse
 
shanos profile image
Stefan Schulz

ty for the article

another approach could be to store the data in localstorage and clear them on succesful submit