简体   繁体   English

可以在功能组件中声明 react-router v6 路由吗?

[英]Can react-router v6 routes be declared in a functional component?

I have an app which is only accessible past the login, I tried implementing it this way:我有一个只能通过登录访问的应用程序,我尝试以这种方式实现它:

import React, {useState} from 'react';
import {Route, Routes} from 'react-router-dom';

import type Admin from '../models/admin';
import {AdminProvider} from './AdminContext';

import LoginC from './components/LoginC';
import RegisterC from './components/RegisterC';
import HomeC from './components/HomeC';

export const Server = () => {
    const [admin, setAdmin] = useState<Admin | undefined>();

    if (!admin) {
        return (
            <Routes>
                <Route path='/' element={<LoginC setAdmin={setAdmin} />} />
                <Route path='/register' element={<RegisterC setAdmin={setAdmin} />} />
            </Routes>
        );
    }

    return (
        <AdminProvider value={admin}>
            <div>Hello server my old friend!</div>
            <HomeC />
        </AdminProvider>
    );
};

export default Server;

The LoginC.tsx file has a link to the register page: LoginC.tsx文件有一个指向注册页面的链接:

<Link to='/register'>Register instead</Link>

My intent was to return different routes when the user is logged in and when they're not.我的意图是在用户登录和未登录时返回不同的路由。

But I get a 404 (React Router) error when I click on the "Register instead" link.但是,当我单击“改为注册”链接时,出现 404(React Router)错误。

It's correctly matching the "/" route though and displaying the LoginC component.它正确地匹配了“/”路由并显示了LoginC组件。

Do all the routes need to be declared statically?所有路由都需要静态声明吗?

My intent was to return different routes depending on whether the user was set or not.我的意图是根据用户是否设置返回不同的路线。

EDIT:编辑:

Here is the code creating the router:这是创建路由器的代码:

import React from 'react';
import {createRoot} from 'react-dom/client';

import {ThemeProvider} from '@mui/material';

import {
    createBrowserRouter as createRouter,
    RouterProvider,
} from 'react-router-dom';

import theme from './theme';
import Server from './Server';

const router = createRouter([
    {
        path: '/',
        element: <Server />,
    },
]);

const elt = document.getElementById('app');

if (!elt) {
    throw new Error('No element with id "app" found');
}

createRoot(elt).render((
    <React.StrictMode>
        <ThemeProvider theme={theme}>
            <RouterProvider router={router} />
        </ThemeProvider>
    </React.StrictMode>
));

Alright, I needed to not create the routes in createBrowserRouter and use BrowserRouter directly as such:好吧,我不需要在createBrowserRouter中创建路由,而是直接使用BrowserRouter

import React from 'react';
import {createRoot} from 'react-dom/client';

import {ThemeProvider} from '@mui/material';

import {BrowserRouter} from 'react-router-dom';

import theme from './theme';
import Server from './Server';

const elt = document.getElementById('app');

if (!elt) {
    throw new Error('No element with id "app" found');
}

createRoot(elt).render(
    <React.StrictMode>
        <ThemeProvider theme={theme}>
            <BrowserRouter>
                <Server />
            </BrowserRouter>
        </ThemeProvider>
    </React.StrictMode>
);

The issue is that the Server is rendered on "/" and attempting to render descendent routes.问题是Server"/"上呈现并试图呈现后代路由。 In order for descendent routes to be matched and rendered the parent route necessarily needs to append the "*" wildcard matcher to its path.为了匹配和呈现后代路由,父路由必须将"*"通配符匹配到其路径。 The LoginC component rendered because the path was will "/" , but "/register" was unable to be matched and render RegisterC .由于路径为"/"而呈现的LoginC组件,但无法匹配"/register"并呈现RegisterC

const router = createRouter([
  {
    path: '/*', // <-- wildcard allows descendent route matching
    element: <Server />,
  },
]);

A more common route protection pattern is to use layout routes and components that check a protection condition and render an Outlet component or redirect to a non-protected route.一种更常见的路由保护模式是使用布局路由和组件来检查保护条件并呈现Outlet组件或重定向到不受保护的路由。

Example:例子:

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

const AdminLayout = ({ admin }) => {
  return admin
    ? (
        <AdminProvider value={admin}>
          <Outlet />
        </AdminProvider>
      )
    : <Navigate to="/login" replace />;
};
export const Server = () => {
  const [admin, setAdmin] = useState<Admin | undefined>();

  return (
    <Routes>
      <Route element={<AdminLayout admin={admin} />}>
        <Route path="/" element={<HomeC />} />
      </Route>
      <Route path='/login' element={<LoginC setAdmin={setAdmin} />} />
      <Route path='/register' element={<RegisterC setAdmin={setAdmin} />} />
    </Routes>
  );
};

And since it doesn't appear any of the RRDv6.4 Data APIs are being used, just render Server into a router.由于没有出现任何 RRDv6.4 数据 API 被使用,只需将Server呈现为路由器即可。

import { BrowserRouter } from 'react-router-dom';

createRoot(elt).render((
  <React.StrictMode>
    <ThemeProvider theme={theme}>
      <BrowserRouter>
        <Server />
      </BrowserRouter>
    </ThemeProvider>
  </React.StrictMode>
));

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM