admin管理员组

文章数量:1404606

I am starting a new React project and want to use the latest version of React Router. The docs remend using createBrowserRouter for all web projects. They don't say why it is better than using BrowserRouterother than it enables some data APIs. Is this the only advantage.

Additionally there is a note in the docs saying

Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the Remixing React Router blog post and the When to Fetch conference talk.

I'm not exactly sure what they mean by outside the React tree isn't everything in my project in the React tree.

Currently I am implementing my router like this:

Main.tsx

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

App.js

function App({ msalContext }: AppProps) {
  return (
    <>
      <AuthenticatedTemplate>
        <Router currentUser={msalContext.accounts[0]} />
      </AuthenticatedTemplate>
    </>
  )
}

Router.js

function Router({ currentUser }: Props) {
  const userInfo = { name: currentUser.name }

  const router = createBrowserRouter([
    {
      path: '/:appTypeId?',
      element: <AppLayout currentUser={userInfo} />,
      loader: defaulNewAPIs,
      children: [
        {
          index: true,
          element: <ReferralDetails status="new" />,
        },
      ],
    },
  ])

  return <RouterProvider router={router} />
}

This doesn't seem to be outside the React tree but I am not exactly sure what would.

I am starting a new React project and want to use the latest version of React Router. The docs remend using createBrowserRouter for all web projects. They don't say why it is better than using BrowserRouterother than it enables some data APIs. Is this the only advantage.

Additionally there is a note in the docs saying

Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the Remixing React Router blog post and the When to Fetch conference talk.

I'm not exactly sure what they mean by outside the React tree isn't everything in my project in the React tree.

Currently I am implementing my router like this:

Main.tsx

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

App.js

function App({ msalContext }: AppProps) {
  return (
    <>
      <AuthenticatedTemplate>
        <Router currentUser={msalContext.accounts[0]} />
      </AuthenticatedTemplate>
    </>
  )
}

Router.js

function Router({ currentUser }: Props) {
  const userInfo = { name: currentUser.name }

  const router = createBrowserRouter([
    {
      path: '/:appTypeId?',
      element: <AppLayout currentUser={userInfo} />,
      loader: defaulNewAPIs,
      children: [
        {
          index: true,
          element: <ReferralDetails status="new" />,
        },
      ],
    },
  ])

  return <RouterProvider router={router} />
}

This doesn't seem to be outside the React tree but I am not exactly sure what would.

Share Improve this question asked Jan 26, 2024 at 13:57 user11631308user11631308 5
  • 1 Please ask one question per question, not more than one. – T.J. Crowder Commented Jan 26, 2024 at 14:01
  • Re your second question, "I'm not exactly sure what they mean by outside the React tree...", they mean in code that isn't in a ponent. For instance, code in main.tsx that you run prior to calling render on the root. (It doesn't have to be main.tsx, it just has to be not in a ponent.) – T.J. Crowder Commented Jan 26, 2024 at 14:02
  • @T.J.Crowder how would this be possible when I need my router to be wrapped in the <AuthenticationTemplate/> ponent – user11631308 Commented Jan 26, 2024 at 15:16
  • Looking at the documentation, it looks like you use RouterProvider there, passing in the router you created outside it, rather than using Router. But I haven't used the latest yet, I was just explaining what they meant in that sentence. – T.J. Crowder Commented Jan 26, 2024 at 15:49
  • This is what I love about Stack Overflow: people need help, and people offer help. Thank you for this detailed, precise question. :) (I especially mend you for your transparency around your thought process and assumptions, like "isn't everything in my project in the React tree" -- so helpful for people who want to answer you!) – Lars Kemmann Commented Oct 8, 2024 at 23:08
Add a ment  | 

1 Answer 1

Reset to default 5

Additionally there is a note in the docs saying

Due to the decoupling of fetching and rendering in the design of the data APIs, you should create your router outside of the React tree with a statically defined set of routes. For more information on this design, please see the Remixing React Router blog post and the When to Fetch conference talk.

I'm not exactly sure what they mean by outside the React tree isn't everything in my project in the React tree.

What this basically means is that is it remended to create the router outside any React ponent such that it is a stable reference.

Example:

const router = createBrowserRouter([
  ...routes...
]);

function App() {
  return <RouterProvider router={router} />;
}

In your implementation the router is created inside the Router ponent.

function Router({ currentUser }: Props) {
  const userInfo = { name: currentUser.name }

  const router = createBrowserRouter([
    {
      path: '/:appTypeId?',
      element: <AppLayout currentUser={userInfo} />,
      loader: defaulNewAPIs,
      children: [
        {
          index: true,
          element: <ReferralDetails status="new" />,
        },
      ],
    },
  ]);

  return <RouterProvider router={router} />;
}

Router is rendered by App.

function App({ msalContext }: AppProps) {
  return (
    <>
      <AuthenticatedTemplate>
        <Router currentUser={msalContext.accounts[0]} />
      </AuthenticatedTemplate>
    </>
  );
}

Any time a ponent higher up in the ReactTree than Router, e.g. AuthenticatedTemplate or App, rerenders, their entire sub-ReactTree is rerendered. What this means in Router now is that the router will be redeclared and a new reference and this will necessarily remount any of the routed ponents that may be rendered.

Since your router appears to have a dependency on the currentUser prop a solution here could be to memoize the router value.

function Router({ currentUser }: Props) {
  const router = React.useMemo(() => {
    return createBrowserRouter([
      {
        path: '/:appTypeId?',
        element: <AppLayout currentUser={{ name: currentUser.name }} />,
        loader: defaulNewAPIs,
        children: [
          {
            index: true,
            element: <ReferralDetails status="new" />,
          },
        ],
      },
    ]);
  }, [currentUser]);

  return <RouterProvider router={router} />;
}

A better solution however would be to provide the currentUser or the entire msalContext value via a React context to your app and decouple if from your routing logic.

Example:

const MsalContext = React.createContext( .... );

const useMsalContext = () => React.useContext();

const MsalContextProvider = ({ children, msalContext }) => {
  return (
    <MsalContext.Provider value={msalContext}>
      {children}
    </MsalContext.Provider>
  );
};
function App({ msalContext }: AppProps) {
  return (
    <MsalContextProvider msalContext={msalContext}>
      <AuthenticatedTemplate>
        <Router />
      </AuthenticatedTemplate>
    </MsalContextProvider>
  )
}
const router = createBrowserRouter([
  {
    path: '/:appTypeId?',
    element: <AppLayout />,
    loader: defaulNewAPIs,
    children: [
      {
        index: true,
        element: <ReferralDetails status="new" />,
      },
    ],
  },
]);

function Router() {
  return <RouterProvider router={router} />;
}
const AppLayout = () => {
  const {
    accounts: [
      { name } // msalContext.accounts[0].name -> name
    ]
  } = useMsalContext();

  ...
};

本文标签: javascriptReact Router using createBrowserRouter vs BrowserRouterStack Overflow