Search code examples
reactjsreact-hooksreact-router-dom

React Router Dom - v6 - useBlocker


As https://github.com/remix-run/react-router/issues/8139 is finished and we got useBlocker in v6, did anyone got it to work?

This is what I got so far and pretty much I'm stuck with error I quite don't understand

in App.js I have my BrowserRouter and everything is wrapped inside

Also I used example from implementer's gists (I copy pasted)

import * as React from "react";
import { useBeforeUnload, unstable_useBlocker as useBlocker } from "react-router-dom";

function usePrompt(message, { beforeUnload } = {}) {

let blocker = useBlocker(
  React.useCallback(
     () => (typeof message === "string" ? !window.confirm(message) : false),
  [message]
  )
);
let prevState = React.useRef(blocker.state);

React.useEffect(() => {
    if (blocker.state === "blocked") {
    blocker.reset();
  }
  prevState.current = blocker.state;
}, [blocker]);

useBeforeUnload(
     React.useCallback(
       (event) => {
         if (beforeUnload && typeof message === "string") {
          event.preventDefault();
          event.returnValue = message;
         }
      },
      [message, beforeUnload]
    ),
  { capture: true }
 );
}

function Prompt({ when, message, ...props }) {
    usePrompt(when ? message : false, props);
    return null;
}

And then within my component I called Prompt like this

const MyComponent = (props) => {
    const [showPrompt, setShowPrompt] = useState(false)

    ...

    return (
        ...
        <Prompt when={showPrompt} 
                message="Unsaved changes detected, continue?" 
                beforeUnload={true} 
        />
    )
}

And on page load of MyComponent I keep getting error

Error: useBlocker must be used within a data router.  See 
    https://reactrouter.com/routers/picking-a-router.
     at invariant (history.ts:308:1)
     at useDataRouterContext (hooks.tsx:523:1)
     at useBlocker (hooks.tsx:723:1)
     at usePrompt (routerCustomPrompt.js:8:1)
     at Prompt (routerCustomPrompt.js:37:1)

Did anyone got useBlocker in new version to work?


Solution

  • The error message is rather clear. In order to use the useBlocker hook it must be used within a component rendered by a Data router. See Picking a Router.

    Example:

    const MyComponent = (props) => {
      const [showPrompt, setShowPrompt] = useState(false);
    
      ...
    
      return (
        ...
        <Prompt
          when={showPrompt} 
          message="Unsaved changes detected, continue?" 
          beforeUnload={true} 
        />
      );
    }
    
    import {
      createBrowserRouter,
      createRoutesFromElements,
      Route,
      RouterProvider,
    } from "react-router-dom";
    
    const router = createBrowserRouter(
      createRoutesFromElements(
        <Route path="/" element={<Root />}>
          {/* ... etc. */}
          <Route path="myComponent" element={<MyComponent />} />
          {/* ... etc. */}
        </Route>
      )
    );
    
    const App = () => <RouterProvider router={router} />;