[V6] [Feature] Getting `usePrompt` and `useBlocker` back in the router

13
open
callmeberzerker
callmeberzerker
Posted 1 month ago

[V6] [Feature] Getting `usePrompt` and `useBlocker` back in the router #8139

I think in general most people won't be able to upgrade to v6 since in the latest beta usePrompt and useBlocker are removed.

Most apps rely on the usePrompt or Prompt to prevent navigation in case the user has unsaved changes (aka the form is dirty).

With this issue maybe we can have some feedback on why they (usePrompt, Prompt) were removed (they worked fine in the previous beta version v6.0.0-beta.6) and what makes them problematic, what's the outlook for getting them back in the router and potentially userland solutions to this problem.

openscript
openscript
Created 1 month ago

I tried to find a workaround with useEffect with the location as a dependencies, but couldn't get it working. With many applications we are stuck on 6.beta.1 because of this and #8035.

KubaOczko
KubaOczko
Created 1 month ago

Is there any guidance on how to implement the same behavior? I didn't find any extension point within the current router implementation. We can't continue with v6 without the possibility to block navigation based on some condition 😢

fgatti675
fgatti675
Created 1 month ago

Removing usePrompt and useBlocker in the 7th iteration of the beta was really unexpected. It would be great to get an alternative way to do the same. Is there any way we can include the removed code in our own codebase? Thanks

dohomi
dohomi
Created 1 month ago

I also experienced this and just found out it happened very unexpected in beta7. +1 for bringing it back or at least outline some workaround.

piecyk
piecyk
Created 1 month ago

As react-router expose NavigationContext under UNSAFE_NavigationContext we can add useBlocker hook back using the same code.

https://github.com/remix-run/react-router/commit/256cad70d3fd4500b1abcfea66f3ee622fb90874#diff-b60f1a2d4276b2a605c05e19816634111de2e8a4186fe9dd7de8e344b65ed4d3L344-L381

import * as React from 'react'
import { UNSAFE_NavigationContext } from 'react-router-dom'
import type { History, Blocker, Transition } from 'history'

export function useBlocker(blocker: Blocker, when = true): void {
  const navigator = React.useContext(UNSAFE_NavigationContext).navigator as History

  React.useEffect(() => {
    if (!when) return

    const unblock = navigator.block((tx: Transition) => {
      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])
}

Don't exactly know why "block" method was omitted from History type? @mjackson maybe we could add it back?

mjackson
mjackson
Created 1 month ago

If you really need blocking I'd recommend you remain on v5 until we have time to get it back into v6. v5 will continue to be supported for the foreseeable future. You shouldn't feel any pressure to upgrade to v6 immediately.

As for why it was removed in v6, we decided we'd rather ship with what we have than take even more time to nail down a feature that isn't fully baked. We will absolutely be working on adding this back in to v6 at some point in the near future, but not for our first stable release of 6.x.

mjackson
mjackson
Created 1 month ago

I might add (for those of you who want to upgrade immediately to v6) that you could consider another type of user experience instead of blocking navigation away from the current page.

The canonical use case for blocking is so you can show the user a prompt to confirm navigation away from a page with unsaved data. Instead of blocking navigation, another valid way to handle this situation could be to just save the state of the form to local/session storage in the browser as the form values change. Then, when the user returns, repopulate the default values of all the form fields with the ones you saved from the last visit. This experience should be less jarring than throwing up a prompt in front of the user while still giving you the resilience you're looking for.

Again, we are planning on re-adding this functionality to v6 after our stable release. I only offer this suggestion as a possible alternative for those of you who may wish to upgrade immediately.

piecyk
piecyk
Created 1 month ago

As for why it was removed in v6, we decided we'd rather ship with what we have than take even more time to nail down a feature that isn't fully baked.

Yes, that's completely understandable. Looking forward for v6 release, great work 👏

My question was about this change, with omitting 'block' from Navigator

Screenshot 2021-10-29 at 06 51 28
mjackson
mjackson
Created 1 month ago

@piecyk You can always create your own type to add that method back. I simply removed all the methods from the History interface that aren't being used anywhere in the router.

yanickrochon
yanickrochon
Created 3 weeks ago

A version with breaking changes notice would've been nice prior to a major release. Right now, the project recommends to upgrade, yet the migration guide is partial and application critical features have been removed without notice.

(After updating to v6, I am currently debugging a few errors with react-router-dom : "TypeError: pathname.match is not a function", and "TypeError: meta.relativePath.startsWith is not a function". The migration is anything but smooth. Compared to this, @material-ui, now @mui, made an excellent job at facilitating the upgrade process, starting a few months ago.)

Previous