Skip to content

Instantly share code, notes, and snippets.

Embed
What would you like to do?
Adds back in `useBlocker` and `usePrompt` to `react-router-dom` version 6.0.2 (they removed after the 6.0.0 beta, temporarily)
/**
* 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>
);
};
@onionhammer
Copy link

onionhammer commented Dec 28, 2021

Would be cool if this would only prompt if 'MyComponent was no longer going to be rendered as a result of continuing - for example, you could be navigating a child component, leaving the form completely intact

@AdnanTheExcellent
Copy link

AdnanTheExcellent commented Jan 3, 2022

i am getting a TS2339: Property 'block' does not exist on type 'Navigator'. error when using typescript.

@WhiteYaksha
Copy link

WhiteYaksha commented Jan 5, 2022

@AdnanTheExcellent It seems like it's a typescript issue, you need to add "block" to the interface definition here

image

@hackerghost93
Copy link

hackerghost93 commented Jan 18, 2022

@WhiteYaksha so to add this i need to fork the repo and add this manually?

@jsmcconkey
Copy link

jsmcconkey commented Mar 25, 2022

@hackerghost93 - I'm sure you got this sorted out by now, but I used patch package myself. If you install patch-package you can make the fix. Then you can run the patch-package tool. Then whenever your modules are installed the patch will then be applied. you can read more about his package at the following link.

https://www.npmjs.com/package/patch-package

@leminhhung-NamiQ
Copy link

leminhhung-NamiQ commented Mar 28, 2022

it's not the best way to do, who else has another way, of change node modules it will fail, please help, thanks

@jsmcconkey
Copy link

jsmcconkey commented Mar 28, 2022

@leminhhung-NamiQ that is highly dependent on the type of change that you do to the node module and the context of that change.

From my point of view this is a small change to a type interface applied to a specific version of a the react-router module. Yes, at some point in the future this patch might fail, however, the risk is minimal. The way I see it at the moment is you can:

  1. Update the type interface through patching the package.
  2. Ignore the type error for now.
  3. Fork the repo and make the change yourself.

If you plan to fork the repo you might as well patch the package. If you lock your node module to a specific version and then watch for updates to react router you should be fine. Based upon this comment it would appear the intention is to bring "block" back.

remix-run/react-router#8139 (comment)

@julix-unity
Copy link

julix-unity commented Apr 8, 2022

Can't you just extend the type and use as?

@matrey
Copy link

matrey commented May 2, 2022

There is a working Typescript version here: https://stackoverflow.com/a/71587163/8046487

import { History, Transition } from 'history';
  • tx is a Transition
  • as for navigator:
type ExtendNavigator = Navigator & Pick<History, "block">;
[...]
const unblock = (navigator as any as ExtendNavigator).block(...

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment