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.
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;
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;
Advantages of Using useCallbackPrompt
- Customizable Prompt: Fully customizable UI for the navigation dialog.
- Dynamic Control: Easily enable or disable navigation blocking based on conditions.
- Retry Mechanism: Automatically retries blocked navigation after confirmation.
- 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
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;
3. Customize the Modal
You can replace the modal with a design from your favorite UI library (e.g., Material-UI, TailwindCSS).
Use Cases
Form Protection
Prevent users from accidentally leaving a page with unsaved changes.Critical Actions
Confirm before performing destructive actions like deleting data.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! 🚀
Top comments (0)