Why do we need to wrap the BrowserRouter in parent component instead of routes child component for react router v6

Viewed 14

I tried to create useRoutes hook from react-router-dom@6.4.1 to create my App routes using JS, like this in my routes.jsx:

import { useRoutes,  } from "react-router-dom";
import TestPage from "../Pages/Public/TestPage";
import TestPageTwo from "../Pages/Public/TestPageTwo";

const redirectRoutes = [
    {
        path: "/no-match",
        element: () => <div>No Match found</div>,
    },
];

const Router = () => {
    const router = useRoutes([
        {
            path: "/testPage",
            element: <TestPage />,
            loader: false,
        },
        {
            path: "/testPageTwo",
            element: <TestPageTwo />,
            loader: false,
        },
        ...redirectRoutes,
    ]);
    return <BrowserRouter>{router}</BrowserRouter>;
};

export default Router;

and in my app.jsx, I imported the file like so:

import { Provider } from "react-redux";
import Router from "./Router";
import initializeStore from "./Reducer/initializeStore";
import "./App.css";

export const store = initializeStore();

const App = () => {
    return (
        <Provider store={store}>
            <Router />
        </Provider>
    );
};

export default App;

This resulted in the following error:

Uncaught Error: useRoutes() may be used only in the context of a component.

As you can see, I have wrapped the routes using BrowserRouter in the app.jsx, but I still get this error. However, I was able to solve this when I moved the BrowserRouter to app.jsx instead of routes.jsx, like so:

app.jsx:

...
const App = () => {
    return (
        <Provider store={store}>
            <BrowserRouter>
                <Router />
            </BrowserRouter>
        </Provider>
    );
};
...

and, in routes.jsx, I just return the router which is created using useRoutes();

I don't understand why do we need to wrap the BrowserRouter from react-router-dom@6 in the app.jsx like this instead of routes.jsx.

1 Answers

In react-router and react-router-dom the router components are the component that provides a routing context for all RRD hooks and components to access. It's a React context and needs to be provided higher in the ReactTree than any of the components that are consuming it.

Doesn't work:

const Router = () => {
  const router = useRoutes([ // <-- accessed here outside context, error!!
    ....
  ]);
  return <BrowserRouter>{router}</BrowserRouter>; // <-- provided here
};

...

const App = () => {
  return (
    <Provider store={store}>
      <Router />
    </Provider>
  );
};

Works:

const Router = () => {
  const router = useRoutes([ // <-- accessed here inside context, no issue :)
    ....
  ]);
  return router;
};

...

const App = () => {
  return (
    <Provider store={store}>
      <BrowserRouter> // <-- provided here
        <Router />
      </BrowserRouter>
    </Provider>
  );
};

See React Context API for more details.

Related