Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[v6] - useBlocker - Calling retry during render phase does not unblock navigation #7734

Closed
mkaizad opened this issue Jan 6, 2021 · 1 comment

Comments

@mkaizad
Copy link

mkaizad commented Jan 6, 2021

Outline

Not sure if this is a bug, but calling the retry method of history.block during render phase does not unblock navigation.

Version

React Router v6.0.0-beta.0

Test Case

The demo app has two pages Step1 and Step2. Step1 has a checkbox called 'Block navigation', which blocks navigation attempts when checked, and a 'Next' button that takes the user to Step2.

Blocking is done using useBlocker inside a wrapper hook called useCustomPrompt. The blocker callback passed to 'useBlocker' is called handleNavigation - it saves the retry method that is passed to it in a ref and displays a modal with a message to accompany the block. In this case, the message displayed is "Are you sure you want to leave?" with 'OK' and 'Cancel' buttons. The user clicks 'OK' to confirm navigation to Step2 (retry is called here) or 'Cancel' to cancel the navigation and remain on Step1.

**Link to demo**

Steps to reproduce

  1. Check 'Block navigation' checkbox
  2. Click 'Next'
  3. Click the 'OK' button inside the "Are you sure you want to leave" prompt.

Expected Behavior

Navigation to Step2 is unblocked and reattempted when the user clicks 'OK'.

Actual Behavior

Navigation is reattempted but not unblocked, so the user remains on Step1 after clicking 'OK'.

Workaround

Calling retry after the render phase inside a useEffect works (link to demo).

Please correct me if I'm wrong but this seems to work due to the clean up of the effect in useBlocker, which calls unblock on clean up (see line 307 react-router/index.tsx and line 587 of history/index.ts). I tried setting up breakpoints to capture the effects:

  • lbuu2.csb.app/example.js? [sm] - lines 30 and 42
  • lbuu2.csb.app/node_modules/react-router/index.js - lines 554, 558, 562, 563
  • lbuu2.csb.app/node_modules/history/index.js - lines 234, 235

When you click 'OK', a state variable proceed is toggled to true, which triggers a re-render. After the re-render, the clean up function of the useBlocker effect seems to be invoked and we go to line 234 (which clears the blockers array) and then to line 235 (which removes the beforeunload event since blockers.length === 0). Then my effect fires (line 42) and calls the retry method. But by this time blockers.length is already 0, so the call to unblock on line 562 is redundant and the transition is allowed to go through at the retry on line 563.

(As an aside, it's not clear what's causing the effect to clean up after the user clicks 'OK', since none of the dependencies [navigator, blocker, when] are changed by the user clicking 'OK'. Clicking 'Next' also triggers this clean up. I'd be grateful if someone could shed some light on why this is happening.)

@mkaizad mkaizad changed the title useBlocker - Calling retry during render does not unblock navigation [v6] - useBlocker - Calling retry during render phase does not unblock navigation Jan 6, 2021
@timdorr
Copy link
Member

timdorr commented Dec 10, 2021

Closing since this API is no longer in the library. #8139 will track when it's added back.

@timdorr timdorr closed this as completed Dec 10, 2021
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants