remix-run / react-router

Declarative routing for React
https://reactrouter.com
MIT License
52.77k stars 10.22k forks source link

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

Closed callmeberzerker closed 1 year ago

callmeberzerker commented 2 years ago

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.

xiaoyijun commented 2 years ago

We have a workaround in the open source identity solution project Logto. You may check out the UnsavedChangesAlertModal for some inspiration.

TrustyTechSG commented 2 years ago

2 hour wasted... roll backed to v5.

lacazeto commented 2 years ago

Perhaps not the most elegant solution, but it is the farthest I've come so far. Still testing for edge cases. I was able to come up with a hook that would prevent ppl from closing / leaving the page or navigating away with/ without react router when the page is "dirty". I used @xiaoyijun post as reference.

import { UNSAFE_NavigationContext as NavigationContext, Navigator, useNavigate } from "react-router-dom";
import { useContext, useEffect, useRef } from "react";
import type { Blocker } from "history";

type BlockerNavigator = Navigator & {
  location: Location;
  block: (blocker: Blocker) => () => void;
};

function beforeUnloadHandler(e: BeforeUnloadEvent) {
  e.preventDefault();
  e.returnValue = "";
  return "Are you sure you want to leave?\n\nChanges that you made may not be saved";
}

function usePreventPageLoad(pageIsDirty: boolean) {
  const { navigator } = useContext(NavigationContext);
  const unblockRef = useRef(function () {
    [].forEach((e) => e);
  });

  const navigate = useNavigate();

  useEffect(() => {
    if (!pageIsDirty) {
      return;
    }

    // To prevent window / tab from closing
    window.addEventListener("beforeunload", beforeUnloadHandler);

    // To prevent react-router-dom from navigating to another page
    const { block } = navigator as BlockerNavigator;
    unblockRef.current = block((transition) => {
      const {
        location: { pathname: targetPathname },
        action,
      } = transition;

      window.removeEventListener("beforeunload", beforeUnloadHandler);

      if (window.confirm("Change page?\n\nChanges that you made may not be saved.")) {
        unblockRef.current();

        // We use this to make it work with navigations without react-router (ie. Browser's go back button)
        if (action.toLowerCase() === "pop") {
          navigate(targetPathname);
        } else {
          // We use this to make it work with navigations via react-router (ie. navLink)
          transition.retry();
        }
      }
    });

    return () => window.removeEventListener("beforeunload", beforeUnloadHandler);
  }, [navigator, navigate, pageIsDirty]);

  return unblockRef;
}

export default usePreventPageLoad;

From the component where u're using it, you can unblock the page if needed (like before doing a form submit)

function MyComponent() {
  const [hasUnsavedChanges, setHasUnsavedChanges] = useState(false);
  const unblockRef = usePreventPageLoad(hasUnsavedChanges);
  ...
  unblockRef.current(); // unblock if necessary before moving to another page
  ...
}

Let me know if anyones sees some major concerns with that approach!

flq commented 2 years ago

@lacazeto - I've taken your code after some examination and it is performing well. What we had to add is that the unblockRef.current() is also called in the cleanup of useEffect, otherwise it was staying active even though the "dirty" flag changes back to false,

lacazeto commented 2 years ago

@lacazeto - I've taken your code after some examination and it is performing well. What we had to add is that the unblockRef.current() is also called in the cleanup of useEffect, otherwise it was staying active even though the "dirty" flag changes back to false,

Yeah, well spot! In my case, that was overlooked as I was never setting dirty back to false again. Just cared to know if the form was interacted somehow and that was it!

But thx for pointing it out!

xdivby0 commented 2 years ago

I went with a mixture of all those solutions, basically, but one thing I didn't quite figure out yet is how to prevent the form submission itself from being blocked. If I use an onSubmit on the form and do setDirty(false) it's a race condition because this is not a synchronous thing.

How did you guys solve it? I feel like waiting a few milliseconds before letting the submission go on is a very cheap hack...

lacazeto commented 2 years ago

I went with a mixture of all those solutions, basically, but one thing I didn't quite figure out yet is how to prevent the form submission itself from being blocked. If I use an onSubmit on the form and do setDirty(false) it's a race condition because this is not a synchronous thing.

How did you guys solve it? I feel like waiting a few milliseconds before letting the submission go on is a very cheap hack...

In my example, I would do smt like e.preventDefault() + unblockRef.current() + postData(payload) and navigate somewhere else upon success.

xdivby0 commented 2 years ago

In my example, I would do smt like e.preventDefault() + unblockRef.current() + postData(payload) and navigate somewhere else upon success.

is unblockRef.current() synchronous in this example? I can't unblock synchronously since I am using the usePrompt from (this)[https://github.com/remix-run/react-router/issues/8139#issuecomment-1147980133] example. I pass a useState variable to the usePrompt which means I can't really instantly set it to false again.

factoidforrest commented 2 years ago

I realize this thread should mainly be about the shortcomings of react-router v6 and how to fix them, but for anyone looking to prevent the back button, I came up with a seemingly very robust solution using native browser methods. My use case was a component that hides itself when the back button is pressed once, without routing the page. This would be pretty useful for prompts as well.

https://stackoverflow.com/a/73312578/650775

Essentially, you can push a fake history event onto the history, and then listen for when it gets popped in order to do something

  function closeQuickView() {
    closeMe() // do whatever you need to close this component
  }

  useEffect(() => {
    // Add a fake history event so that the back button does nothing if pressed once
    window.history.pushState('fake-route', document.title, window.location.href);

    addEventListener('popstate', closeQuickView);

    // Here is the cleanup when this component unmounts
    return () => {
      removeEventListener('popstate', closeQuickView);
      // If we left without using the back button, aka by using a button on the page, we need to clear out that fake history event
      if (window.history.state === 'fake-route') {
        window.history.back();
      }
    };
  }, []);
jeremy-code commented 2 years ago

hello gamers i have realized a very cool solution i think.

so the issue is that the navigator thingy won't let u block anymore bcuz they got rid of the method for it but if u just wrap it around the History type, u get to have all its methods back

basically:

// it picks only the methods from histroy that are those
export declare type Navigator = Pick<History, "go" | "push" | "replace" | "createHref">;

```javascript
// u get the mback
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'
import type { History } from 'history'

const { navigator } = useContext(NavigationContext)
const blockNavigator = navigator as History

this is jsut a quick implementation of a usePrompt hook:

import { useState, useContext, useEffect } from 'react'
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'
import type { History } from 'history'

export const usePrompt = () => {
  const [isOpen, setIsOpen] = useState(false)
  const { navigator } = useContext(NavigationContext)
  const blockNavigator = navigator as History

  useEffect(() => {
    const unblock = blockNavigator.block((tx) => {
      setIsOpen(true)
      if (!setIsOpen) {
        unblock()
        tx.retry()
      }
    })
  }, [blockNavigator])

  return [isOpen, setIsOpen] as const
}

which gives you the state and a getter if you want to make a custom modal or something, you can just change if (!setIsOpen) to if (window.confirm(text)) if you just want the window confirmation thing

qianlongdoit commented 2 years ago

hello gamers i have realized a very cool solution i think.

so the issue is that the navigator thingy won't let u block anymore bcuz they got rid of the method for it but if u just wrap it around the History type, u get to have all its methods back

basically:

// it picks only the methods from histroy that are those
export declare type Navigator = Pick<History, "go" | "push" | "replace" | "createHref">;

```javascript
// u get the mback
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'
import type { History } from 'history'

const { navigator } = useContext(NavigationContext)
const blockNavigator = navigator as History

this is jsut a quick implementation of a usePrompt hook:

import { useState, useContext, useEffect } from 'react'
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom'
import type { History } from 'history'

export const usePrompt = () => {
  const [isOpen, setIsOpen] = useState(false)
  const { navigator } = useContext(NavigationContext)
  const blockNavigator = navigator as History

  useEffect(() => {
    const unblock = blockNavigator.block((tx) => {
      setIsOpen(true)
      if (!setIsOpen) {
        unblock()
        tx.retry()
      }
    })
  }, [blockNavigator])

  return [isOpen, setIsOpen] as const
}

which gives you the state and a getter if you want to make a custom modal or something, you can just change if (!setIsOpen) to if (window.confirm(text)) if you just want the window confirmation thing

it‘s very helpful to me. Based on your code, I made a little change. It's easier to use

import { useState, useContext, useEffect, useRef, useCallback } from 'react';
import { UNSAFE_NavigationContext as NavigationContext } from 'react-router-dom';
import type { History } from 'history';

/** @description Blocks all navigation attempts.
 *  @param when {boolean}  Whether to start intercepting navigation.
 *  @example
 *  const [flag, setFlag, next] = usePrompt(false);
 *  useEffect(() => {
 *     if (flag) {
 *  //     do something like show a dialog etc;
 *  //     at the right time resume bocked navigate
 *         next();
 *     }
 *   }, [flag]);

 */
export const usePrompt = (when = false) => {
  const [flag, setFlag] = useState(false);
  const confirm = useRef<any>(null);
  const context = useRef<any>(null);
  const { navigator } = useContext(NavigationContext);
  const blockNavigator = navigator as History;

  const next = useCallback(() => {
    confirm.current();
    context.current?.retry?.();
  }, [flag]);

  useEffect(() => {
    if (!when) return;
    const unblock = blockNavigator.block((tx) => {
      setFlag(true);
      context.current = tx;
    });
    confirm.current = unblock;
    return unblock;
  }, [blockNavigator, when]);

  return [flag, setFlag, next] as const;
};
xr0master commented 2 years ago

@qianlongdoit blockNavigator.block is not a function unfortunately. Sources don't have this feature either.

bestLessons commented 2 years ago

ave realized a very cool solution i think.

I've also added some updates, in my case I don't want to block routing if some of the query parameters changes. It might not work for all the cases but should give you an idea of how to do it

import { isEqual } from 'lodash';
import { useState, useContext, useEffect, useRef, useCallback } from 'react';
import { UNSAFE_NavigationContext as NavigationContext, useLocation } from 'react-router-dom';
import { usePrevious } from 'react-use';
import { searchStrToObj } from '../helpers/jsHelper';

/** @description Blocks all navigation attempts.
 *  @param when {boolean}  Whether to start intercepting navigation.
 *  @param skip {boolean| { queryParams: string[] }} Wheter we want to skip preventing if some part of url was changed (for example if changing query parameter doesn't mean that we reload the page)
 *  @example
 *  const [flag, setFlag, next] = usePrompt(false);
 *  useEffect(() => {
 *     if (flag) {
 *  //     do something like show a dialog etc;
 *  //     at the right time resume bocked navigate
 *         next();
 *     }
 *   }, [flag]);

 */

export const usePrompt = (when = false, { skip } = {}) => {
  const [flag, setFlag] = useState(false);
  const confirm = useRef(null);
  const context = useRef(null);
  const location = useLocation();
  const { navigator } = useContext(NavigationContext);
  const blockNavigator = navigator;
  const locationRef = useRef(null)
  locationRef.current = location;

  const next = useCallback(() => {
    confirm.current();
    context.current?.retry?.();
  }, [flag]);

  const shouldSkip = (newLocation) => {
    if (!skip) return false;

    if (typeof skip === 'boolean') {
      return skip;
    }

    if (locationRef.current.pathname !== newLocation.pathname) {
      return false;
    }

    const { queryParams } = skip;

    const currentSearch = searchStrToObj(locationRef.current.search)
    const newSearch = searchStrToObj(newLocation.search);

    queryParams.forEach(key => {
      delete currentSearch[key];
      delete newSearch[key];
    })

    return  isEqual(currentSearch, newSearch)
  }

  useEffect(() => {
    if (!when) return;
    const unblock = blockNavigator.block((tx) => {
      if (shouldSkip(tx.location)) {
        confirm.current?.()
        tx.retry?.();
        return;
      }

      setFlag(true);
      context.current = tx;
    });
    confirm.current = unblock;
    return unblock;
  }, [blockNavigator, when]);

  return [flag, setFlag, next];
};

// inside your component 
const [flag, setFlag, next] = usePrompt(!curatorSaved, { skip: { queryParams: ['cameraName'] } });

  useEffect(() => {
    if (flag) {
      openInfoDialog({
        title: 'Do you want to leave current project?',
        description: 'All not saved changes will be lost',
        cancelButtonText: 'No',
        buttonText: 'Yes',
        onCancelClick: () => {
          setFlag(false);
        },
        onButtonClick: () => {
          setFlag(false);
          next();
        },
      });
    }
  }, [flag]);

// helpers
export const searchStrToObj = (str) => {
  const s = new URLSearchParams(str)
  return Object.fromEntries(s);
}
qianlongdoit commented 2 years ago

want to block routing

@qianlongdoit blockNavigator.block is not a function unfortunately. Sources don't have this feature either.

@xr0master It works in version 6.3.0, update your dependcies and see whether it works?

wojtekmaj commented 2 years ago

@qianlongdoit, it has already been removed in 6.4.0 pre release.

avasuro commented 2 years ago

Damn. Is route blocking is still a feature that react-router developers are going to support in the future? Looks like it's not getting closer, but incontrary it's getting worse and worse from month to month. Probably somebody knows, how to clarify plans of this library developers for this feature?

xr0master commented 2 years ago

@avasuro I also don’t understand why, with such stubborn confidence, developers destroy all workarounds without an alternative. I would love to stay on version 5, but this version does not support React 18...

salimkanoun commented 2 years ago

@avasuro I also don’t understand why, with such stubborn confidence, developers destroy all workarounds without an alternative. I would love to stay on version 5, but this version does not support React 18...

We are working on V5 with react 18 and we experience no issues at all. (and we are waiting form prompt to migrate to v6)

wojtekmaj commented 2 years ago

@avasuro I also don’t understand why, with such stubborn confidence, developers destroy all workarounds without an alternative. I would love to stay on version 5, but this version does not support React 18...

@xr0master I locked version on ~6.3.0. 6.3.0 still provides navigator.block, so you can build useBlocker yourself e.g. following https://github.com/remix-run/react-router/issues/8139#issuecomment-1023105785.

xr0master commented 2 years ago

@wojtekmaj Thanks, but I have to have the relative="path" feature.

xr0master commented 2 years ago

We are working on V5 with react 18 and we experience no issues at all. (and we are waiting form prompt to migrate to v6)

[off-topic] Oh! Thanks, I tried again, removed the node_modules and the lock file and a fresh install was able to install the v5 router with react 18! It really works. But without the strict mode. [/off-topic]

westprophet commented 2 years ago

Guys are you kidding me? A little more and it will be 1 year for this issue.

When will this issue be resolved? I don’t even see workarounds yet, maybe someone will tell you please how to make analogues of these hooks?

esetnik commented 2 years ago

Confirmed we cannot upgrade to v6.4.0 due to the removal of navigator.block. The versioning scheme for this project seems to be broken when they update the minor version from v6.3.0 to v6.4.0 and create a breaking change.

TrejGun commented 2 years ago

removing of navigator.block breaks a lot of sites and should be marked as major change, not minor.

heath-freenome commented 2 years ago

removing of navigator.block breaks a lot of sites and should be marked as major change, not minor.

Agreed! At this point I'm looking for an alternative to react-router since they broke my app (AGAIN) and this time with a point version. Goodbye React-Router... It's too bad that you didn't want to get the Prompt function working again. This was a KEY feature that my company relies on.

piecyk commented 2 years ago

Just fyi, it's still possible to use navigator.block via history package with v6.4.0, by replacing BrowserRouter with unstable_HistoryRouter, upgrade steps

import React from 'react';
import ReactDOM from 'react-dom/client';
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { createBrowserHistory } from 'history';

import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HistoryRouter history={createBrowserHistory({ window })}>
      <App />
    </HistoryRouter>
  </React.StrictMode>
);

https://stackblitz.com/edit/github-fqzmgn-ktacur-historyrouter

@westprophet checkout the example

xr0master commented 2 years ago

Purely technically it is open source and we cannot demand anything. We can only do PR ourselves. Of course, it's annoying that a rather serious feature was removed without explanation. Because of this, it is not even clear what kind of PR to do. The second problem is the lack of alternatives for React, or am I wrong?

sjdemartini commented 2 years ago

@xr0master react-location may be an option for some folks, as suggested a bit earlier (https://github.com/remix-run/react-router/issues/8139#issuecomment-1046330015)

Beej126 commented 2 years ago

for the recent folks chiming in with frustration, make sure you read all the earlier comments in this thread... including taking care to expand the ones github will be automatically hiding from you to lower the volume at this point... there's several of us that have been free sailing with easy workarounds since the beginning of the year, e.g. mine is only 50 lines of code back in April.

heath-freenome commented 2 years ago

for the recent folks chiming in with frustration, make sure you read all the earlier comments in this thread... including taking care to expand the ones github will be automatically hiding from you to lower the volume at this point... there's several of us that have been free sailing with easy workarounds since the beginning of the year, e.g. mine is only 50 lines of code back in April.

Those workarounds (including mine) hinge on getting access to the navigator.block() API which has been removed from 6.4.0... Good luck upgrading

piecyk commented 2 years ago

@heath-freenome you can still use navigator.block via history package, checkout the https://github.com/remix-run/react-router/issues/8139#issuecomment-1247080906

yarinsa commented 2 years ago

Any fix in the near future? Is it in planning for the next major version of react-route?

thkim9012 commented 2 years ago

Official document says "It will be added in the near future." however, the hope is gone in v6.4.0

westprophet commented 2 years ago
unstable_HistoryRouter 

I have a very large application that has complex referencing.

I need HashRouter, not HistoryRouter

But, thanks for your answer

piecyk commented 2 years ago

I need HashRouter, not HistoryRouter

@westprophet HashRouter is just HistoryRouter with createHashHistory from history package

ChrisSong1994 commented 2 years ago

Just fyi, it's still possible to use navigator.block via history package with v6.4.0, by replacing BrowserRouter with unstable_HistoryRouter, upgrade steps

  • add history package
  • use unstable_HistoryRouter, rather than BrowserRouter
import React from 'react';
import ReactDOM from 'react-dom/client';
import { unstable_HistoryRouter as HistoryRouter } from 'react-router-dom';
import { createBrowserHistory } from 'history';

import App from './App';

ReactDOM.createRoot(document.getElementById('root')).render(
  <React.StrictMode>
    <HistoryRouter history={createBrowserHistory({ window })}>
      <App />
    </HistoryRouter>
  </React.StrictMode>
);
  • add hook that use navigator.block, like useBlocker

https://stackblitz.com/edit/github-fqzmgn-ktacur-historyrouter

@westprophet checkout the example

it's very useful to me , good job!

zzZCodeZzz commented 1 year ago

Quote: 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.

Where banana?

pantirugabriel commented 1 year ago

Has this issue been fixed? I see a merge into master from above pull request.

acarroll-trend commented 1 year ago

@pantirugabriel That merge was into a different repo. This issue is still open.

MaybeRex commented 1 year ago

I hope this is added back in for the next release,

wojtekmaj commented 1 year ago

I realize it's a bit ironic to write this and trigger all the notifications myself, but

Please do not reply here if you have nothing constructive to add.

Obviously, with 400+ reactions this is a heated topic. We know this, the maintainers know this. Things like:

"Any updates?"

"Is this going to be included in the next release?"

"+1"

are NOT gonna speed anything up. But there's one thing these replies will do: trigger notifications for 100s of people subscribed to this thread. If you'd like to be notified about the progress, if any, hit Subscribe button on the right hand side.

chaance commented 1 year ago

Removing navigator.block in 6.4 was not a breaking change from 6.3 because navigator.block was never a part of the v6 API. I know it's frustrating for those who were been using it as a workaround for removed v5 APIs, but as @mjackson noted above v6 only exposed a subset of the history object's API. Relying on internal implementation details was never what we advised. I'll take it a step further and advise that you should not rely on implementation details, and doing so means you accept any risk that your code will break in future updates.

For those looking for an update: we've spent almost a year rethinking what we might do with useBlocker or usePrompt. We hoped we'd be able to add it back, but history blocking was always somewhat half-baked and—in too many cases—leads to broken, buggy user experiences.

Our primary goal with OSS is to help folks build better software, and if a feature fails to meet that goal then we probably shouldn't ship it. Learning these lessons is part of the process, and though painful, breaking major changes are sometimes required to move the project forward.

Given what we've learned, we can't guarantee that these patterns will make it back into future releases. As such our recommendations are:

In the mean time, we are working on some demos and updates to our documentation to show specific patterns we think are more appropriate than blocking history. I'll be sure to update the thread as those are completed.

code-jongleur commented 1 year ago

I understand the decision. Sometimes it is needed to throw away old stuff that became a barrier. We had this in our projects often enough, too.

In the meanwhile we decided for our projects where we use v6 to remove all code based on usePrompt/useBlocker and conceptually rework the user interface, because the „unsaved changes“ dialogs where we needed it for were not really great in UX. And the generic web browser messages that popped up in some situations were even worse.

Since we automatically save every relevant stuff in our web app, the UX is better and we can use full-powered v6. There are even situations where saving the last user input is not needed at all and not a real loss. The code is better readable and better maintainable if we compare it to the previous version.

Maybe conceptually changing your user interfaces might also be a way for some of you.

ericchernuka commented 1 year ago

@code-jongleur mind sharing how you keep the form state persisted? Do you use sessionStorage to avoid having to manage cleaning it up? Is there any times you ran into situations where you needed a time stamp or something to check how long ago they saved this state?

I'd love to move to this strategy but wondered about when to throw a at the users previous form state.

code-jongleur commented 1 year ago

mind sharing how you keep the form state persisted? Do you use sessionStorage to avoid having to manage cleaning it up? Is there any times you ran into situations where you needed a time stamp or something to check how long ago they saved this state?

It depends on the application and context. Usually the user is logged in and there is a session etc. The work is saved almost immediately when the user makes some changes. We collect the changes in the frontend and then send a save request.

The form data/user's work is sent to the backend and next time, he visits the page with the same url, loaded again. That's also nice when the user closes the browser in accident and re-opens it. Then he can continue the work without any break. I do not remember that we need a timestamp for this, only in scenarios where a document is locked for a special user and unlocked after a period of time.

Sometimes it's enough to use the local storage, eg for very user specific stuff like last collapse or expand state of some panels or for input elements that are only relevant for the current user. That's also an option. We have a mixture of both. Hope my answer helps you somehow.

xr0master commented 1 year ago

I don't want to be rude to anyone, this is open source and everyone does what they think is right, but it sounds funny: "We've been thinking about how to do this for a whole year (!!!), because we didn't think about it during technical design, and now it’s like a hole in the head."

Furthermore, funny true stories about UX and how cool that this feature is removed. @code-jongleur If calling a built-in block function causes more complexity in your code than writing a draft state or a sync, you have a big problem with app architecture. I'm glad you had the chance to rewrite it all.

Now let's turn off the cynical mode and discuss how to make such functionality. @ericchernuka you can immediately synchronize the new state with the server if your project logic allows it. This is important because any accidental click/press on any input will immediately go into "production". If this is not possible, you need to develop a draft state. It is necessary to indicate somewhere in a VERY noticeable place a banner/alert that this view is in the draft state so that the client understands that what he sees is not in "production" yet.

Where to store the draft state depends on your application and how important the loss of information is. The sessionStorage is cleared when the user closes the browser window (not the tab!!!). If it is too short, then it is better to use the localStorage. It can also be cleared by OS, but this doesn't happen often.

Probably the most difficult thing is restoring the draft state after opening the view. If the view's state has been changed from somewhere else, then your draft state is in conflict. How to solve it again depends on your application, if possible it is better to drop the draft state if the version on the server has changed.

As you can see, creating a draft state is much more complicated than calling an "ugly built-in popup" in one line. It requires a logical decision of the product manager, as well as a large number of changes in the application. Undoubtedly, this improves UX, but requires resources, instead of developing more useful things.

Good luck with your architecture! And once again I want to say thanks to the contributors of this library, they do it for free, please do not forget about it.

P.S. We have decided to stick with version 5 for now, despite various warnings from NPM about package incompatibilities.

image
dietergeerts commented 1 year ago

removing of navigator.block breaks a lot of sites and should be marked as major change, not minor.

Agreed! At this point I'm looking for an alternative to react-router since they broke my app (AGAIN) and this time with a point version. Goodbye React-Router... It's too bad that you didn't want to get the Prompt function working again. This was a KEY feature that my company relies on.

Give UI Router a try, it's one of the best routers out there! https://ui-router.github.io/

chaance commented 1 year ago

@xr0master re: dependency warnings, https://github.com/remix-run/react-router/pull/9382 should get rid of that if we can cut a quick patch. Seems like that would be the only one triggered by us.

crates commented 1 year ago

Just a heads-up: the folks maintaining React Admin aren't updating their code because they think you will be reintroducing this method. Since it looks like you won't, I guess someone should let @fzaninotto know that a different approach is needed in order to stay compatible with the newest versions of React Router.

heath-freenome commented 1 year ago

As an FYI, there are more use cases for block than just "put up a dialog". I have an implementation of a wizard that will prevent the user from moving forward or backward when there is a condition that requires the user to fix something, such as waiting for an API to finish, dealing with API errors, etc.