Skip to main content

Command Palette

Search for a command to run...

Unsaved Changes Warning in React

Updated
3 min read
Unsaved Changes Warning in React
M

Hi, I am Nahib. Working as a software engineer at ImpleVista.

In this tutorial, we'll explore how to implement an unsaved change warning in a React application. Instead of relying on third-party libraries, we'll build a custom hook that's both lightweight and tailored to our needs.

Getting Started

You can either clone the project from GitHub to use the base template or follow along with your existing setup.

For this project, we've used Vite for efficiency and Tailwind for styling.

Building the Comment Form

Let's start by creating a basic form named CommentForm with two states: name and comment. Upon clicking the button, it will display a success or failure message based on whether the form is filled.

import { useState } from "react";
import { useUnsavedChangeWarning } from "../hooks/useUnsavedChangeWarning";

const CommentForm = () => {
  const [name, setName] = useState<string>("");
  const [comment, setComment] = useState<string>("");

  useUnsavedChangeWarning(!name || !comment);

  return (
    <div className="flex justify-center items-center h-[100vh]">
      <div className="md:w-[600px] max-w-md p-6 bg-white border border-gray-200 rounded-lg shadow">
        <h5 className="mb-2 text-2xl font-semibold tracking-tight text-gray-900 ">
          Comment Now
        </h5>
        <div className="flex flex-col">
          <label htmlFor="name" className="my-2">
            Your name
          </label>
          <input
            id="name"
            name="name"
            placeholder="Your Name"
            className="shadow-sm bg-gray-50 border border-gray-30 p-2.5"
            value={name}
            onChange={(e) => setName(e.target.value)}
            autoComplete="off"
          />
        </div>
        <div className="flex flex-col">
          <label htmlFor="comment" className="my-2">
            Your Comment
          </label>
          <textarea
            id="comment"
            name="comment"
            value={comment}
            onChange={(e) => setComment(e.target.value)}
            placeholder="Your Comment"
            className="shadow-sm bg-gray-50 border border-gray-30 p-2.5 w-full"
          ></textarea>
        </div>
        <button
          className="bg-blue-500 hover:bg-blue-700 px-2 py-3 mt-4 shadow rounded-lg text-white"
          onClick={() => {
            if (name && comment) alert("Successful!");
            else alert("Failed!");
          }}
        >
          Comment Now
        </button>
      </div>
    </div>
  );
};

export default CommentForm;

Next, import CommentForm into your App.tsx:

// App.tsx
import CommentForm from "./components/CommentForm";

export default function App() {
  return <CommentForm />;
}

The Image below refers to the design of the page.

Creating the Unsaved Changes Warning Hook

Now, let's build a custom hook named useUnsavedChangeWarning. Inside the hooks folder, create a new file named useUnsavedChangeWarning.tsx:

// useUnsavedChangeWarning.tsx

import { useEffect } from "react";

const handleUnsavedEvent = (e: BeforeUnloadEvent, condition: boolean) => {
  if (condition) {
    e.preventDefault();
    e.returnValue = true;
  }
};

export const useUnsavedChangeWarning = (isUnsaved: boolean) => {
  useEffect(() => {
    window.addEventListener("beforeunload", (e) =>
      handleUnsavedEvent(e, isUnsaved)
    );
    return () =>
      window.removeEventListener("beforeunload", (e) =>
        handleUnsavedEvent(e, isUnsaved)
      );
  }, [isUnsaved]);
};

This hook takes a boolean parameter isUnsaved to determine whether there are unsaved changes. It adds an event listener for the beforeunload event, triggering the handleUnsavedEvent function. The cleanup function removes the event listener on component unmount or when isUnsaved changes.

Integrating the Hook

Import and use the useUnsavedChangeWarning hook in CommentForm:

// CommentForm.tsx
import React, { useState } from "react";
import useUnsavedChangeWarning from "../hooks/useUnsavedChangeWarning";

const CommentForm = () => {
  const [name, setName] = useState("");
  const [comment, setComment] = useState("");

  useUnsavedChangeWarning(!name || !comment);

  // Rest of the form code...
};

export default CommentForm;

With this implementation, your React application will now display a warning when users attempt to close the window without saving changes. This custom hook approach keeps your codebase clean and free from unnecessary dependencies.

Feel free to customize it further to suit your specific requirements.

Remember to share your thoughts or improvements in the comments below! Happy coding!