Skip to content

Instantly share code, notes, and snippets.

@buYoung
Last active February 2, 2024 04:38
Show Gist options
  • Save buYoung/cd68cc1982888e3fbf27ab6ba6c160ca to your computer and use it in GitHub Desktop.
Save buYoung/cd68cc1982888e3fbf27ab6ba6c160ca to your computer and use it in GitHub Desktop.
Custom Hook for React Router v6 Before the Addition of useBlocker
// use case useStateWithCallbackLazy for blocking Status
function Ui(props) {
const [promptStatus, setPromptStatus] = useStateWithCallbackLazy(true);
const [showPrompt, confirmNavigation, cancelNavigation] = useCallbackPrompt(promptStatus);
const handleExit = () => {
confirmNavigation();
setPromptStatus(false, () => {
navigate(pageUrl });
});
};
return (
<div>
<button onClick={handleExit}>exit</button>
</div>
)
}
// use case useState for blocking Status
function Ui2(props) {
const [promptStatus, setPromptStatus] = useState(true);
const [showPrompt, confirmNavigation, cancelNavigation] = useCallbackPrompt(promptStatus);
const handleExit = () => {
confirmNavigation();
setPromptStatus(false);
};
useEffect(() => {
if (!promptStatus) {
showPrompt();
}
}, [promptStatus])
return (
<div>
<button onClick={handleExit}>exit</button>
</div>
)
}
import { useEffect, useContext } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';
import type { History, Blocker, Transition } from 'history';
export interface UseBlockerProps {
blocker: Blocker;
when?: boolean;
}
export const useBlocker = ({ blocker, when }: UseBlockerProps): void => {
const navigator = useContext(NavigationContext).navigator as History;
useEffect(() => {
if (!when) return;
const unblock = navigator.block((tx: Transition) => {
const autoUnblockingTx = {
...tx,
retry() {
unblock();
tx.retry();
},
};
blocker(autoUnblockingTx);
});
return unblock;
}, [navigator, blocker, when]);
};
import { useCallback, useState } from 'react';
import { useLocation } from 'react-router-dom';
import type { Transition } from 'history';
import { useBlocker } from 'hooks/useBlocker';
export interface UseCallbackPromptProps {
when: boolean;
}
export interface UseCallbackPrompt {
showPrompt: boolean;
confirmNavigation: () => void;
cancelNavigation: () => void;
}
export const useCallbackPrompt = ({ when }: UseCallbackPromptProps): UseCallbackPrompt => {
const location = useLocation();
const [showPrompt, setShowPrompt] = useState(false);
const [blockedLocation, setBlockedLocation] = useState<Transition | null>(null);
const cancelNavigation = useCallback(() => {
setShowPrompt(false);
setBlockedLocation(null);
}, []);
const blocker = useCallback(
(tx: Transition) => {
if (tx.location.pathname !== location.pathname) {
setBlockedLocation(tx);
setShowPrompt(true);
}
},
[location],
);
const confirmNavigation = useCallback(() => {
if (blockedLocation) {
blockedLocation.retry();
cancelNavigation();
}
}, [blockedLocation]);
useBlocker({ blocker, when });
return { showPrompt, confirmNavigation, cancelNavigation };
};
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment