skip to Main Content

Im new to react routers and currently using the BrowserRouter from react-router-dom with the version react-router-dom": "^6.21.0"
Im using the useLocation() to dynmaically set a store variable whenever the path changes.

Here is what im doing:

App.tsx:

function App() {
    const location = useLocation();
    const setMode= useStore((state) => state.setMode);
    useEffect(() => {
        setMode(location.pathname.includes(Constants.AppRoutes.IMAGE_PATH));
    }, [location.pathname, setMode]);

    const routesBody = (
        <div className="main">
            <Comp1/>
            <Comp2/>
            <Comp3/>
        </div>
    );

    const routes = [
        { path: "/", element: <Navigate replace to="bla1" /> },
        {
            path: "/bla1",
            element: routesBody,
        },
        {
            path: "/bla2",
            element: routesBody,
        },
    ];

    return (
        <BrowserRouter>
            <Routes>
                {routes.map((route) => (
                    <Route key={route.path} path={route.path} element={route.element} />
                ))}
            </Routes>
        </BrowserRouter>
    );
}

export default App;

Main.tsx:

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
       <App /> 
    </React.StrictMode>,
);

This setup causes the following error

Uncaught Error: useLocation() may be used only in the context of a <Router> component.

What I did:

Moving the <BrowserRouter> tag from the app.tsx to the main.tsx to make the main.tsx look like this:

ReactDOM.createRoot(document.getElementById('root')!).render(
    <React.StrictMode>
        <BrowserRouter>
            <App />
        </BrowserRouter>
    </React.StrictMode>,
);

While this seems to solve the issue and the routing works it causes all my test cases to have the following clause when rendering otherwise all the test cases will fail to render.

render(
            <BrowserRouter>
                <App />
            </BrowserRouter>,
        );

Why cant i just put the <BrowserRouter> in my App.tsx?
Am i missing something?

2

Answers


  1. Chosen as BEST ANSWER

    Solved by using the solution found here

    The code looks like this now:

    const AppLayout = () => {
      const location = useLocation();
    
      useEffect(() => {
        //do stuff with the location. 
      }, [location]);
    
      return (
        <div>
                <Comp1/>
                <Comp2/>
                <Comp3/>
        </div>
      );
    };
    
    const router = createBrowserRouter(
      createRoutesFromElements(
        <>
          <Route path="/" element={<Navigate replace to="/bla1"/>} />
          <Route path="/bla1" element={<AppLayout />} />
          <Route path="/bla2" element={<AppLayout />} />
        </>
      )
    );
    
    function App() {
      return <RouterProvider router={router} />;
    }
    

  2. useLocation uses a React Context provided by BrowserRouter. In order to be able to read from a Context, a component need have the Context provider (in this case, BrowserRouter) as an ancestor of the React tree.

    As for your test cases, you could either

    1. extract some utility function that wraps your App inside BrowserRouter for you.
    2. Add a layer so that App contains BrowserRouter:
      function App() {
        return (
           <BrowserRouter>
              <Root />
          </BrowserRouter>
        );
      }
      
      function Root() {
         const location = useLocation();
         ...
      }
      
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search