Home > front end >  How to solve conflict between react-router-dom v6 and mobx?
How to solve conflict between react-router-dom v6 and mobx?

Time:03-04

I've created dynamic routing on my site, which changes when a user login successfully. The fact of logging I keep in global state, which observers by mobx. When the user login successfully, routes changes too, and it works correctly, but in the console, there is the next problem: Error

Error in text variant:

react-dom.development.js:67 Warning: React has detected a change in the order of Hooks called by AppRouter. This will lead to bugs and errors if not fixed. For more information, read the Rules of Hooks: https://reactjs.org/link/rules-of-hooks

Previous render Next render

  1. useState useState
  2. useState useState
  3. useRef useRef
  4. useDebugValue useDebugValue
  5. useEffect useEffect
  6. useContext useContext
  7. undefined useContext ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

There is a screenshot of the route's component: Routes component Routes component code:

import { observer } from "mobx-react-lite";
import { useContext } from "react";
import { Navigate, Route, Routes } from "react-router-dom";
import { Context } from "../..";
import { adminPaths, guestPaths, userPaths } from "./paths";
const AppRouter = () => {
  const { userStore } = useContext(Context);
  return (
    <Routes>
      <Route path='/*' element={<Navigate to='/' />} />
      {
        userStore.isAuth && userPaths.map(({ path, component }) => 
          <Route path={path} element={component()} />)
      }
      {
        userStore.isAuth && adminPaths.map(({ path, component }) => 
          <Route path={path} element={component()} />)
      }
      {
        guestPaths.map(({ path, component }) => <Route path={path} element={component()} />)
      }
    </Routes>
 )
}
export default observer(AppRouter);

When I remove the observer in this component, error disappeared, but routes don't update after login.

Routes configuration code: Routes configuration Routes configuration code:

import AdminCabinet from "../../pages/admin-cabinet/admin-cabinet";
import HomePage from "../../pages/home-page/home-page";
import UserCabinet from "../../pages/user-cabinet/user-cabinet";

export const guestPaths = [
    {
        name: 'Home',
        path: '/',
        component: HomePage
    }
];
export const userPaths = [
    {
        name: 'Personal cabinet',
        path: '/personalCabinet',
        component: UserCabinet
    }
];
export const adminPaths = [
    {
        name: 'Admin cabinet',
        path: '/adminCabinet',
        component: AdminCabinet
    }
];

I would be grateful if someone helps me with this problem.

CodePudding user response:

Issue

The only overt issue I see with your code is that you are directly invoking your React components instead of rendering them as JSX for React to handle and manage the component lifecycle of.

Example:

import UserCabinet from "../../pages/user-cabinet/user-cabinet";

const userPaths = [
  {
    name: "Personal cabinet",
    path: "/personalCabinet",
    component: UserCabinet,
  },
];

...

const AppRouter = () => {
  const { userStore } = useContext(Context);

  return (
    <Routes>
      ...
      {userStore.isAuth && userPaths.map(({ path, component }) => (
        <Route path={path} element={component()} /> // <-- invoking component
      ))}
      ...
    </Routes>
  );
};

Solution

The element prop should receive JSX. When destructuring component rename it to Component so it has a valid React component name and render as JSX. Don't forget to use a valid React key for the mapped routes.

Example:

import UserCabinet from "../../pages/user-cabinet/user-cabinet";

const userPaths = [
  {
    name: "Personal cabinet",
    path: "/personalCabinet",
    component: UserCabinet,
  },
];

...

const AppRouter = () => {
  const { userStore } = useContext(Context);

  return (
    <Routes>
      ...
      {userStore.isAuth && userPaths.map(({ path, component: Component }) => (
        <Route
          key={path}
          path={path}
          element={<Component />} // <-- pass as JSX
        />
      ))}
      ...
    </Routes>
  );
};
  • Related