-
-
Save rmorse/426ffcc579922a82749934826fa9f743 to your computer and use it in GitHub Desktop.
| /** | |
| * These hooks re-implement the now removed useBlocker and usePrompt hooks in 'react-router-dom'. | |
| * Thanks for the idea @piecyk https://github.com/remix-run/react-router/issues/8139#issuecomment-953816315 | |
| * Source: https://github.com/remix-run/react-router/commit/256cad70d3fd4500b1abcfea66f3ee622fb90874#diff-b60f1a2d4276b2a605c05e19816634111de2e8a4186fe9dd7de8e344b65ed4d3L344-L381 | |
| */ | |
| import { useContext, useEffect, useCallback } from 'react'; | |
| import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'; | |
| /** | |
| * Blocks all navigation attempts. This is useful for preventing the page from | |
| * changing until some condition is met, like saving form data. | |
| * | |
| * @param blocker | |
| * @param when | |
| * @see https://reactrouter.com/api/useBlocker | |
| */ | |
| export function useBlocker( blocker, when = true ) { | |
| const { navigator } = useContext( NavigationContext ); | |
| useEffect( () => { | |
| if ( ! when ) return; | |
| const unblock = navigator.block( ( tx ) => { | |
| const autoUnblockingTx = { | |
| ...tx, | |
| retry() { | |
| // Automatically unblock the transition so it can play all the way | |
| // through before retrying it. TODO: Figure out how to re-enable | |
| // this block if the transition is cancelled for some reason. | |
| unblock(); | |
| tx.retry(); | |
| }, | |
| }; | |
| blocker( autoUnblockingTx ); | |
| } ); | |
| return unblock; | |
| }, [ navigator, blocker, when ] ); | |
| } | |
| /** | |
| * Prompts the user with an Alert before they leave the current screen. | |
| * | |
| * @param message | |
| * @param when | |
| */ | |
| export function usePrompt( message, when = true ) { | |
| const blocker = useCallback( | |
| ( tx ) => { | |
| // eslint-disable-next-line no-alert | |
| if ( window.confirm( message ) ) tx.retry(); | |
| }, | |
| [ message ] | |
| ); | |
| useBlocker( blocker, when ); | |
| } |
| const MyComponent = () => { | |
| const formIsDirty = true; // Condition to trigger the prompt. | |
| usePrompt( 'Leave screen?', formIsDirty ); | |
| return ( | |
| <div>Hello world</div> | |
| ); | |
| }; |
@bufke did you find a way to solve this with createBrowserRouter ?
I did not, I ended up using <BrowserRouter>.
This approach may not be needed soon as we now have (as of v6.7.0) unstable_useBlocker and unstable_usePrompt:
remix-run/react-router#8139 (comment)
Waiting for the docs to be written before I update this gist.
Not woking with some gaming mouse have back button, any ideals?
Here's a useBeforeUnload hook for v6.4.3 that catches both react router navigation and browser navigation:
import { useCallback } from 'react';
import { useBeforeUnload as _useBeforeUnload, unstable_usePrompt as usePrompt } from 'react-router-dom';
export default function useBeforeUnload(doBlock?: boolean) {
_useBeforeUnload(
useCallback(e => {
if (doBlock) {
e.preventDefault();
return e.returnValue = '';
}
}, [doBlock])
);
usePrompt({
when: doBlock,
message: 'Discard unsaved changes?'
});
}
thx!
Here's a useBeforeUnload hook for v6.4.3 that catches both react router navigation and browser navigation:
import { useCallback } from 'react'; import { useBeforeUnload as _useBeforeUnload, unstable_usePrompt as usePrompt } from 'react-router-dom'; export default function useBeforeUnload(doBlock?: boolean) { _useBeforeUnload( useCallback(e => { if (doBlock) { e.preventDefault(); return e.returnValue = ''; } }, [doBlock]) ); usePrompt({ when: doBlock, message: 'Discard unsaved changes?' }); }
This is awesome, thanks for sharing! 💯
you can simply add these:
useEffect(() => {
window.addEventListener("beforeunload", onBeforeUnload);
return () => {
window.removeEventListener("beforeunload", onBeforeUnload);
};
});
const onBeforeUnload = (e) => {
if (hasDirty) {
e.preventDefault();
e.returnValue = "";
}
};
Reference : https://claritydev.net/blog/display-warning-for-unsaved-form-data-on-page-exit
you can simply add these:
useEffect(() => { window.addEventListener("beforeunload", onBeforeUnload); return () => { window.removeEventListener("beforeunload", onBeforeUnload); }; }); const onBeforeUnload = (e) => { if (hasDirty) { e.preventDefault(); e.returnValue = ""; } };Reference : https://claritydev.net/blog/display-warning-for-unsaved-form-data-on-page-exit
This doesn't fire during route transitions...
Is there a way to use unstable_HistoryRouter with createBrowserRouter? The current react router tutorial uses createBrowserRouter.