createBrowserRouter 出现错误:useNavigate() 只能在 <Router> 组件的上下文中使用

qwe*_*ezz 6 typescript reactjs react-router react-router-dom

我试图在 React Router 6.4.3 中从 BrowserRoute 组件切换到 createBrowserRouter 但出现错误:

useNavigate() 只能在组件上下文中使用。

我该如何解决这个问题?该错误来自 AuthProvider 组件,我认为我需要用 RouterProvider 包装它,但不清楚如何做到这一点。

Index.tsx 组件

const container = document.getElementById('root')!;
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <AuthProvider>
        <ThemeProvider theme={theme}>
          <App />
        </ThemeProvider>
      </AuthProvider>
    </Provider>
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

App.tsx 组件

const router = createBrowserRouter([
  {
    element: <ProtectedRoute />,
    children: [
      {
        path: '/',
        element: <DashboardLayout />,
        children: [
          {
            index: true,
            element: <HomePage />,
          },
          {
            path: 'about',
            element: <AboutPage />,
          },
        ],
      },
    ],
  },
  {
    path: '/auth',
    element: <Auth />,
  },
]);

const App = () => {
  const { isLoading } = useAuth0();

  if (isLoading) {
    return <LoaderPage />;
  }

  return (
    <>
      <Box>
        <CssBaseline />
        <RouterProvider router={router} />
      </Box>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

错误来自 AuthProvider.tsx

export const AuthProvider = ({ children }: PropsWithChildren<Auth0ProviderWithConfigProps>): JSX.Element | null => {
  const domain = process.env.REACT_APP_AUTH0_DOMAIN;
  const clientId = process.env.REACT_APP_AUTH0_CLIENT_ID;
  const navigate = useNavigate();
  if (!(domain && clientId)) {
    return null;
  }

  const onRedirectCallback = (appState: any) => {
    navigate(appState?.returnTo || window.location.pathname);
  };

  return (
    <Auth0Provider
      domain={domain}
      clientId={clientId}
      redirectUri={window.location.origin}
      onRedirectCallback={onRedirectCallback}
    >
      {children}
    </Auth0Provider>
  );
};
Run Code Online (Sandbox Code Playgroud)

UPD

受保护的路由.tsx

export const ProtectedRoute: React.FC = () => {
  const location = useLocation();

  const { isAuthenticated } = useAuth0();

  return isAuthenticated? <Outlet /> : <Navigate to="/auth" state={{ from: location }} replace />;
};
Run Code Online (Sandbox Code Playgroud)

如果我回滚,请将这些组件更改为支持 BrowserRouter,然后一切正常:

索引.tsx

root.render(
  <React.StrictMode>
    <BrowserRouter>
      <AuthProvider>
        <Provider store={store}>
          <ThemeProvider theme={theme}>
            <App />
          </ThemeProvider>
        </Provider>
      </AuthProvider>
    </BrowserRouter>
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

应用程序.tsx

const App = () => {
  const { isLoading } = useAuth0();

  const routes = useRoutes([
    {
      element: <ProtectedRoute />,
      children: [
        {
          path: '/',
          element: <DashboardLayout />,
          children: [
            {
              index: true,
              element: <HomePage />,
            },
            {
              path: 'about',
              element: <AboutPage />,
            },
          ],
        },
      ],
    },
    {
      path: '/auth',
      element: <Auth />,
    },
  ]);

  if (isLoading) {
    return <LoaderPage />;
  }

  return (
    <>
      <Box>
        <CssBaseline />
        {routes}
      </Box>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

Codesandbox: https://codesandbox.io/s/nice-morning-oxwwj6?file=/src/ App.tsx

Dre*_*ese 3

AuthProvider组件需要在路由上下文中呈现,以便访问它并使用任何钩子react-router-domAuthProvider创建一个渲染组件包装的布局路线Outlet

例子:

import {
  createBrowserRouter,
  Outlet
} from 'react-router-dom';

const AuthLayout = () => (
  <AuthProvider>
    <Outlet />
  </AuthProvider>
);

const router = createBrowserRouter([
  {
    element: <AuthLayout />,
    children: [
      {
        element: <ProtectedRoute />,
        children: [
          {
            path: '/',
            element: <DashboardLayout />,
            children: [
              {
                index: true,
                element: <HomePage />,
              },
              {
                path: 'about',
                element: <AboutPage />,
              },
            ],
          },
        ],
      },
      {
        path: '/auth',
        element: <Auth />,
      },
    ]
  },
]);

const App = () => {
  const { isLoading } = useAuth0();

  if (isLoading) {
    return <LoaderPage />;
  }

  return (
    <>
      <Box>
        <CssBaseline />
        <RouterProvider router={router} />
      </Box>
    </>
  );
};
Run Code Online (Sandbox Code Playgroud)

...

const container = document.getElementById('root')!;
const root = createRoot(container);

root.render(
  <React.StrictMode>
    <Provider store={store}>
      <ThemeProvider theme={theme}>
        <App />
      </ThemeProvider>
    </Provider>
  </React.StrictMode>
);
Run Code Online (Sandbox Code Playgroud)

  • @qweezz 与需要在路由上下文中呈现的“useNavigate”挂钩类似,“useAuth0”挂钩也需要在“AuthProvider”组件提供的 Auth0 上下文中使用。这是你的 [sandbox](https://codesandbox.io/s/getting-error-with-createbrowserrouter-usenavigate-may-be-used-only-in-the-co-5qy25r) 的一个分支,进行了一个小重构做这个。 (2认同)