Logo

dev-resources.site

for different kinds of informations.

Mastering Navigation Control in React with useCallbackPrompt and useBlocker 🚦

Published at
12/19/2024
Categories
react
reactrouter
webdev
customhooks
Author
hasunnilupul
Author
12 person written this
hasunnilupul
open
Mastering Navigation Control in React with useCallbackPrompt and useBlocker 🚦

Building seamless navigation experiences is essential in modern web applications. Whether you're safeguarding unsaved changes or confirming critical actions, having control over navigation flow ensures a better user experience.

This article introduces useCallbackPrompt, a custom React hook built on useBlocker, that offers enhanced control over navigation events with customizable prompts.

React Route Blocker

What is useCallbackPrompt?

useCallbackPrompt is a custom hook designed to intercept navigation events and display a confirmation prompt before allowing users to navigate away. It's built using the lower-level useBlocker hook, which integrates with React Router's internal navigation APIs.

What is useBlocker?

useBlocker is a utility hook that provides the core blocking mechanism by listening to React Router's navigation events. It enables the creation of custom logic when navigation is attempted.

Here’s the code for useBlocker:

import { useContext, useEffect } from 'react';
import { UNSAFE_NavigationContext } from 'react-router-dom';

const useBlocker = (blocker, enable = true) => {
  const navigator = useContext(UNSAFE_NavigationContext).navigator;

  useEffect(() => {
    if (!enable) return;

    const unblock = navigator.block((tx) => {
      const autoUnblockingTx = {
        ...tx,
        retry() {
          unblock();
          tx.retry();
        },
      };

      blocker(autoUnblockingTx);
    });

    return unblock;
  }, [navigator, blocker, enable]);
};

export default useBlocker;
Enter fullscreen mode Exit fullscreen mode

How useCallbackPrompt Works

useCallbackPrompt extends useBlocker to provide a more user-friendly interface for managing navigation prompts.

Here’s the implementation:

import { useCallback, useEffect, useState } from 'react';
import { useLocation, useNavigate } from 'react-router';
import useBlocker from './useBlocker';

const useCallbackPrompt = (enable) => {
  const navigate = useNavigate();
  const location = useLocation();
  const [showPrompt, setShowPrompt] = useState(false);
  const [lastLocation, setLastLocation] = useState(null);
  const [confirmedNavigation, setConfirmedNavigation] = useState(false);

  // Cancel the navigation
  const cancelNavigation = useCallback(() => {
    setShowPrompt(false);
  }, []);

  // Block navigation and show prompt
  const handleBlockedNavigation = useCallback(
    (nextLocation) => {
      if (
        !confirmedNavigation &&
        nextLocation?.location?.pathname !== location?.pathname
      ) {
        setShowPrompt(true);
        setLastLocation(nextLocation);
        return false;
      }
      return true;
    },
    [confirmedNavigation, location]
  );

  // Confirm the navigation
  const confirmNavigation = useCallback(() => {
    setShowPrompt(false);
    setConfirmedNavigation(true);
  }, []);

  useEffect(() => {
    if (confirmedNavigation && lastLocation) {
      navigate(lastLocation.location.pathname);
      setConfirmedNavigation(false);
    }
  }, [confirmedNavigation, lastLocation, navigate]);

  useBlocker(handleBlockedNavigation, enable);

  return [showPrompt, confirmNavigation, cancelNavigation];
};

export default useCallbackPrompt;
Enter fullscreen mode Exit fullscreen mode

Advantages of Using useCallbackPrompt

  1. Customizable Prompt: Fully customizable UI for the navigation dialog.
  2. Dynamic Control: Easily enable or disable navigation blocking based on conditions.
  3. Retry Mechanism: Automatically retries blocked navigation after confirmation.
  4. Seamless Integration: Works with React Router’s navigation API for modern SPAs.

How to Use useCallbackPrompt

Here’s how you can use useCallbackPrompt in your React app:

1. Install React Router

Ensure React Router is installed:

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

2. Use the Hook

Here’s a form example where the prompt shows when users navigate away with unsaved changes:

import React, { useState } from 'react';
import useCallbackPrompt from './useCallbackPrompt';

const FormPage = () => {
  const [isDirty, setIsDirty] = useState(false);
  const [showPrompt, confirmNavigation, cancelNavigation] = useCallbackPrompt(isDirty);

  return (
    <>
      <form>
        <input
          type="text"
          onChange={() => setIsDirty(true)}
          placeholder="Type something..."
        />
        <button onClick={() => setIsDirty(false)}>Save</button>
      </form>

      {showPrompt && (
        <div className="modal">
          <p>You have unsaved changes. Do you really want to leave?</p>
          <button onClick={confirmNavigation}>Yes</button>
          <button onClick={cancelNavigation}>No</button>
        </div>
      )}
    </>
  );
};

export default FormPage;
Enter fullscreen mode Exit fullscreen mode

3. Customize the Modal

You can replace the modal with a design from your favorite UI library (e.g., Material-UI, TailwindCSS).


Use Cases

  1. Form Protection

    Prevent users from accidentally leaving a page with unsaved changes.

  2. Critical Actions

    Confirm before performing destructive actions like deleting data.

  3. Route Guards

    Dynamically block navigation based on authentication or other app states.


Conclusion

useCallbackPrompt and useBlocker are invaluable tools for managing navigation in React applications. By combining React Router’s navigation API with custom logic, you can build robust user experiences that prioritize safety and usability.

Github Repo: react-navigation-blocker

Let me know if you try these hooks or have questions—happy coding! 🚀

customhooks Article's
29 articles in total
Favicon
Mastering Navigation Control in React with useCallbackPrompt and useBlocker 🚦
Favicon
Custom Hooks in React: Reusing Logic Across Components
Favicon
Fixing UI Update Issues with Custom Hooks in React
Favicon
Custom Hooks in React: A Guide to Creation and Usage
Favicon
React: leveraging custom hooks to extract reusable logic
Favicon
Custom Hooks
Favicon
Custom Hooks in React
Favicon
Mobile Orientation (React-Native)
Favicon
Reusing Logic in React with Custom Hooks: a Practical guide
Favicon
Harnessing Firebase in React with Custom Hooks: A Practical Guide
Favicon
Building Powerful React Components with Custom Hooks
Favicon
React Custom Hooks (useNetworkStatus)
Favicon
React Custom Hooks (useMediaPermission)
Favicon
React Custom Hooks (useDebounce)
Favicon
How to Create a Counter App with useReducer and Custom Hooks
Favicon
How to create custom Hooks in React.Js?
Favicon
React Slideshow Component with autoplay, fullscreen mode and expand all
Favicon
React Portals: create and open modals with keyboard keys
Favicon
Learn Something on React JSX and Hooks
Favicon
How to create Tabs in ReactJs using Hooks ?
Favicon
Simple hook to handle featue flags
Favicon
React custom hooks to update form data
Favicon
useIpAdrress()
Favicon
Custom hook useScroll.tsx :: React TypeScript
Favicon
useQuery() with React Router Dom
Favicon
React Custom Hooks
Favicon
useGeoPosition Hook - A Custom React Hook to grab latitude and longitude from a given address.
Favicon
`useBackButton` hook to handle back button behavior in React Native
Favicon
Form in Modal using React hooks – mistakes and lesson learnt

Featured ones: