-
-
Notifications
You must be signed in to change notification settings - Fork 10.3k
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
fix(react-router-dom): Fix usePrompt
invalid blocker state transition
#10687
fix(react-router-dom): Fix usePrompt
invalid blocker state transition
#10687
Conversation
- Remove the `setTimeout` wrapping `blocker.proceed`. This defers the execution of the function, but I don't think that's what we want here. - Reorder the `useEffect`s so that we attempt to proceed first when appropriate and prevent resetting a blocker that we're about to proceed.
|
Hi @louis-young, Welcome, and thank you for contributing to React Router! Before we consider your pull request, we ask that you sign our Contributor License Agreement (CLA). We require this only once. You may review the CLA and sign it by adding your name to contributors.yml. Once the CLA is signed, the If you have already signed the CLA and received this response in error, or if you have any questions, please contact us at hello@remix.run. Thanks! - The Remix team |
Thank you for signing the Contributor License Agreement. Let's get this merged! 🥳 |
Can we add a test as well? |
Hi 👋 Thanks for taking a look. I looked at the test coverage when making the change and couldn't find any tests for I'm more than happy to write the tests 🙂 Thanks! |
To be frank, That said - more tests is never a bad thing so if they're not too much of a pain to implement (along with mocking |
I agree with all of this. This seems like a reasonable and pragmatic approach 👍 I'll take a look at writing some tests and go from there 🙂 |
I've written a suite of unit tests for A few things to note:
I didn't actually write a test case for the specific use case that surfaced this regression as it didn't fit very nicely into the suite and would essentially be checking that we don't introduce this specific regression again. Is this something that we want to do? If so, is there a convention defined in the codebase for writing test cases like this? I'm more than happy to extend the test suite. What do you think? |
These tests are great @louis-young! Thank you! I made some minor updates in d470e99 - added |
Thank you! I had a look and the tweaks look great, nice one. I forgot that Thanks for putting the changeset together too. This looks good to me. Thanks for your help with this 🎉 |
Ah, wait. I think the In Arc (a Chromium based browser) the path is changing to the previous path but switching back to the current one, when the confirm dialog was closed with Can you reproduce this? Or should I try to come up with a reproducable Sandbox? |
Hi 👋 Please could you open an issue so that we can track this better? If you could provide a minimal reproducible example then that'd be really helpful. Thanks. |
PR for #10489.
This pull request fixes the
usePrompt
invalid blocker state transition invariant exception.Essentially, when navigating, if we should block navigation, we update the blocker for the derived blocker key so that it's in a "blocked" state. In the proceed function for this blocker, we then update the blocker so that it's in a "proceeding" state. In the reset function for this blocker, we reset the blocker to the
IDLE_BLOCKER
(which results in an "unblocked" state).In the
updateBlocker
function, we receive the blocker key as a parameter and update it. What was happening here is that by the time we came to proceed the blocker, it was in a "proceeding" state, causing the invariant to throw an exception according to the state-machine-like logic (logically, as this makes no sense).The blocker was in this state because in the
usePrompt
hook, there are two effects: one that resets the blocker when thewhen
parameter changes, and one that proceeds the blocker after confirmation. These two effects were essentially fighting each other; the first effect was resetting the blocker just before the second effect attempted to proceed it, leading to the unexpected state transition.What I've done (after reasoning about this with brophdawg11, thanks for your time and expertise Matt) is both reversed the definition order of the effects so that we first attempt to proceed a blocker when appropriate, and also removed the
setTimeout
wrapping the call toblocker.proceed
as I think this defers the execution of the function, which probably isn't what we want here.This appears to fix the problem 🎉
Changes
setTimeout
wrappingblocker.proceed
. This defers the execution of the function, but I don't think that's what we want here.useEffect
s so that we attempt to proceed first when appropriate and prevent resetting a blocker that we're about to proceed.Notes
setTimeout
wrappingblocker.proceed
was required, or why anything I've done here doesn't sound like a good idea then please let me know 🙂