[英]Nested routes with react router v4 / v5
I am currently struggling with nesting routes using react router v4.我目前正在努力使用反应路由器 v4 嵌套路由。
The closest example was the route config in the React-Router v4 Documentation .最接近的示例是React-Router v4 文档中的路由配置。
I want to split my app in 2 different parts.我想将我的应用分成 2 个不同的部分。
A frontend and an admin area.一个前端和一个管理区域。
I was thinking about something like this:我在想这样的事情:
<Match pattern="/" component={Frontpage}>
<Match pattern="/home" component={HomePage} />
<Match pattern="/about" component={AboutPage} />
</Match>
<Match pattern="/admin" component={Backend}>
<Match pattern="/home" component={Dashboard} />
<Match pattern="/users" component={UserPage} />
</Match>
<Miss component={NotFoundPage} />
The frontend has a different layout and style than the admin area.前端的布局和样式与管理区域不同。 So within the frontpage the route home, about and so one should be the child routes.
所以在首页的路线首页,关于等应该是子路线。
/home
should be rendered into the Frontpage component and /admin/home
should be rendered within the Backend component. /home
应该呈现在 Frontpage 组件中,而/admin/home
应该呈现在 Backend 组件中。
I tried some other variations but I always ended in not hitting /home
or /admin/home
.我尝试了其他一些变化,但我总是以没有点击
/home
或/admin/home
结束。
This is the final solution I am using right now.这是我现在使用的最终解决方案。 This example also has a global error component like a traditional 404 page.
这个例子还有一个像传统 404 页面一样的全局错误组件。
import React, { Component } from 'react';
import { Switch, Route, Redirect, Link } from 'react-router-dom';
const Home = () => <div><h1>Home</h1></div>;
const User = () => <div><h1>User</h1></div>;
const Error = () => <div><h1>Error</h1></div>
const Frontend = props => {
console.log('Frontend');
return (
<div>
<h2>Frontend</h2>
<p><Link to="/">Root</Link></p>
<p><Link to="/user">User</Link></p>
<p><Link to="/admin">Backend</Link></p>
<p><Link to="/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
<Switch>
<Route exact path='/' component={Home}/>
<Route path='/user' component={User}/>
<Redirect to={{
state: { error: true }
}} />
</Switch>
<footer>Bottom</footer>
</div>
);
}
const Backend = props => {
console.log('Backend');
return (
<div>
<h2>Backend</h2>
<p><Link to="/admin">Root</Link></p>
<p><Link to="/admin/user">User</Link></p>
<p><Link to="/">Frontend</Link></p>
<p><Link to="/admin/the-route-is-swiggity-swoute">Swiggity swooty</Link></p>
<Switch>
<Route exact path='/admin' component={Home}/>
<Route path='/admin/user' component={User}/>
<Redirect to={{
state: { error: true }
}} />
</Switch>
<footer>Bottom</footer>
</div>
);
}
class GlobalErrorSwitch extends Component {
previousLocation = this.props.location
componentWillUpdate(nextProps) {
const { location } = this.props;
if (nextProps.history.action !== 'POP'
&& (!location.state || !location.state.error)) {
this.previousLocation = this.props.location
};
}
render() {
const { location } = this.props;
const isError = !!(
location.state &&
location.state.error &&
this.previousLocation !== location // not initial render
)
return (
<div>
{
isError
? <Route component={Error} />
: <Switch location={isError ? this.previousLocation : location}>
<Route path="/admin" component={Backend} />
<Route path="/" component={Frontend} />
</Switch>}
</div>
)
}
}
class App extends Component {
render() {
return <Route component={GlobalErrorSwitch} />
}
}
export default App;
In react-router-v4 you don't nest <Routes />
.在 react-router-v4 中,您不会嵌套
<Routes />
。 Instead, you put them inside another <Component />
.相反,您将它们放在另一个
<Component />
中。
For instance例如
<Route path='/topics' component={Topics}>
<Route path='/topics/:topicId' component={Topic} />
</Route>
should become应该成为
<Route path='/topics' component={Topics} />
with和
const Topics = ({ match }) => (
<div>
<h2>Topics</h2>
<Link to={`${match.url}/exampleTopicId`}>
Example topic
</Link>
<Route path={`${match.path}/:topicId`} component={Topic}/>
</div>
)
Here is a basic example straight from the react-router documentation .这是直接来自 react-router 文档的基本示例。
react-router v6
Update for 2022 - v6 has nested Route
components that Just Work™. 2022 年更新- v6 包含 Just Work™ 的嵌套
Route
组件。
This question is about v4/v5, but the best answer now is just use v6 if you can!这个问题是关于 v4/v5 的,但现在最好的答案是尽可能使用 v6!
See example code in this blog post .请参阅此博客文章中的示例代码。 If you can't upgrade just yet, however...
但是,如果您还不能升级...
react-router v4 & v5
It's true that in order to nest Routes you need to place them in the child component of the Route.确实,为了嵌套 Route,您需要将它们放置在 Route 的子组件中。
However if you prefer a more inline syntax rather than breaking your Routes up across components, you can provide a functional component to the render
prop of the Route you want to nest under.但是,如果您更喜欢内联语法而不是跨组件分解路由,则可以为要嵌套在其下的 Route 的
render
prop 提供一个功能组件。
<BrowserRouter>
<Route path="/" component={Frontpage} exact />
<Route path="/home" component={HomePage} />
<Route path="/about" component={AboutPage} />
<Route
path="/admin"
render={({ match: { url } }) => (
<>
<Route path={`${url}/`} component={Backend} exact />
<Route path={`${url}/home`} component={Dashboard} />
<Route path={`${url}/users`} component={UserPage} />
</>
)}
/>
</BrowserRouter>
If you're interested in why the render
prop should be used, and not the component
prop, it's because it stops the inline functional component from being remounted on every render.如果您对为什么应该使用
render
属性而不是component
属性感兴趣,那是因为它阻止了内联功能组件在每次渲染时重新安装。 See the documentation for more detail.有关更多详细信息, 请参阅文档。
Note the example wraps the nested Routes in a Fragment .请注意,该示例将嵌套的 Routes 包装在Fragment中。 Prior to React 16, you can use a container
<div>
instead.在 React 16 之前,您可以使用容器
<div>
代替。
Just wanted to mention react-router v4 changed radically since this question was posted/answed.只是想提一下,自从发布/回答了这个问题以来,react-router v4 发生了根本性的变化。
There is no <Match>
component any more!不再有
<Match>
组件! <Switch>
is to make sure only the first match is rendered. <Switch>
是为了确保只渲染第一个匹配项。 <Redirect>
well .. redirects to another route. <Redirect>
好吧 .. 重定向到另一条路线。 Use or leave out exact
to either in- or exclude a partial match.使用或省略
exact
匹配或排除部分匹配。
See the docs.请参阅文档。 They are great.
他们都是伟大的。 https://reacttraining.com/react-router/
https://reacttraining.com/react-router/
Here's an example I hope is useable to answer your question.这是一个例子,我希望可以用来回答你的问题。
<Router>
<div>
<Redirect exact from='/' to='/front'/>
<Route path="/" render={() => {
return (
<div>
<h2>Home menu</h2>
<Link to="/front">front</Link>
<Link to="/back">back</Link>
</div>
);
}} />
<Route path="/front" render={() => {
return (
<div>
<h2>front menu</h2>
<Link to="/front/help">help</Link>
<Link to="/front/about">about</Link>
</div>
);
}} />
<Route exact path="/front/help" render={() => {
return <h2>front help</h2>;
}} />
<Route exact path="/front/about" render={() => {
return <h2>front about</h2>;
}} />
<Route path="/back" render={() => {
return (
<div>
<h2>back menu</h2>
<Link to="/back/help">help</Link>
<Link to="/back/about">about</Link>
</div>
);
}} />
<Route exact path="/back/help" render={() => {
return <h2>back help</h2>;
}} />
<Route exact path="/back/about" render={() => {
return <h2>back about</h2>;
}} />
</div>
</Router>
Hope it helped, let me know.希望它有帮助,让我知道。 If this example is not answering your question well enough, tell me and I'll see if I can modify it.
如果这个例子不能很好地回答你的问题,请告诉我,我会看看我是否可以修改它。
I succeeded in defining nested routes by wrapping with Switch
and define nested route before than root route.我通过使用
Switch
包装成功地定义了嵌套路由,并在根路由之前定义了嵌套路由。
<BrowserRouter>
<Switch>
<Route path="/staffs/:id/edit" component={StaffEdit} />
<Route path="/staffs/:id" component={StaffShow} />
<Route path="/staffs" component={StaffIndex} />
</Switch>
</BrowserRouter>
Reference: https://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md参考: https ://github.com/ReactTraining/react-router/blob/master/packages/react-router/docs/api/Switch.md
The latest update with hooks is to use useRouteMatch
.钩子的最新更新是使用
useRouteMatch
。
export default function NestingExample() {
return (
<Router>
<Switch>
<Route path="/topics">
<Topics />
</Route>
</Switch>
</Router>
);
}
function Topics() {
// The `path` lets us build <Route> paths
// while the `url` lets us build relative links.
let { path, url } = useRouteMatch();
return (
<div>
<h2>Topics</h2>
<h5>
<Link to={`${url}/otherpath`}>/topics/otherpath/</Link>
</h5>
<ul>
<li>
<Link to={`${url}/topic1`}>/topics/topic1/</Link>
</li>
<li>
<Link to={`${url}/topic2`}>/topics/topic2</Link>
</li>
</ul>
// You can then use nested routing inside the child itself
<Switch>
<Route exact path={path}>
<h3>Please select a topic.</h3>
</Route>
<Route path={`${path}/:topicId`}>
<Topic />
</Route>
<Route path={`${path}/otherpath`>
<OtherPath/>
</Route>
</Switch>
</div>
);
}
Some thing like this.像这样的东西。
import React from 'react'; import { BrowserRouter as Router, Route, NavLink, Switch, Link } from 'react-router-dom'; import '../assets/styles/App.css'; const Home = () => <NormalNavLinks> <h1>HOME</h1> </NormalNavLinks>; const About = () => <NormalNavLinks> <h1>About</h1> </NormalNavLinks>; const Help = () => <NormalNavLinks> <h1>Help</h1> </NormalNavLinks>; const AdminHome = () => <AdminNavLinks> <h1>root</h1> </AdminNavLinks>; const AdminAbout = () => <AdminNavLinks> <h1>Admin about</h1> </AdminNavLinks>; const AdminHelp = () => <AdminNavLinks> <h1>Admin Help</h1> </AdminNavLinks>; const AdminNavLinks = (props) => ( <div> <h2>Admin Menu</h2> <NavLink exact to="/admin">Admin Home</NavLink> <NavLink to="/admin/help">Admin Help</NavLink> <NavLink to="/admin/about">Admin About</NavLink> <Link to="/">Home</Link> {props.children} </div> ); const NormalNavLinks = (props) => ( <div> <h2>Normal Menu</h2> <NavLink exact to="/">Home</NavLink> <NavLink to="/help">Help</NavLink> <NavLink to="/about">About</NavLink> <Link to="/admin">Admin</Link> {props.children} </div> ); const App = () => ( <Router> <div> <Switch> <Route exact path="/" component={Home}/> <Route path="/help" component={Help}/> <Route path="/about" component={About}/> <Route exact path="/admin" component={AdminHome}/> <Route path="/admin/help" component={AdminHelp}/> <Route path="/admin/about" component={AdminAbout}/> </Switch> </div> </Router> ); export default App;
A complete answer for React Router v6 or version 6 just in case needed. React Router v6 或版本 6 的完整答案,以备不时之需。
import Dashboard from "./dashboard/Dashboard";
import DashboardDefaultContent from "./dashboard/dashboard-default-content";
import { Route, Routes } from "react-router";
import { useRoutes } from "react-router-dom";
/*Routes is used to be Switch*/
const Router = () => {
return (
<Routes>
<Route path="/" element={<LandingPage />} />
<Route path="games" element={<Games />} />
<Route path="game-details/:id" element={<GameDetails />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="/" element={<DashboardDefaultContent />} />
<Route path="inbox" element={<Inbox />} />
<Route path="settings-and-privacy" element={<SettingsAndPrivacy />} />
<Route path="*" element={<NotFound />} />
</Route>
<Route path="*" element={<NotFound />} />
</Routes>
);
};
export default Router;
import DashboardSidebarNavigation from "./dashboard-sidebar-navigation";
import { Grid } from "@material-ui/core";
import { Outlet } from "react-router";
const Dashboard = () => {
return (
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<DashboardSidebarNavigation />
<Outlet />
</Grid>
);
};
export default Dashboard;
Github repo is here. Github 仓库在这里。 https://github.com/webmasterdevlin/react-router-6-demo
https://github.com/webmasterdevlin/react-router-6-demo
allows to use both nested routes (like in v3) and separate, splitted routes (v4, v5).允许使用嵌套路由(如在 v3 中)和单独的拆分路由(v4、v5)。
Keep all routes in one place for small/medium size apps:对于中小型应用程序,将所有路线放在一个地方:
<Routes>
<Route path="/" element={<Home />} >
<Route path="user" element={<User />} />
<Route path="dash" element={<Dashboard />} />
</Route>
</Routes>
const App = () => { return ( <BrowserRouter> <Routes> // /js is start path of stack snippet <Route path="/js" element={<Home />} > <Route path="user" element={<User />} /> <Route path="dash" element={<Dashboard />} /> </Route> </Routes> </BrowserRouter> ); } const Home = () => { const location = useLocation() return ( <div> <p>URL path: {location.pathname}</p> <Outlet /> <p> <Link to="user" style={{paddingRight: "10px"}}>user</Link> <Link to="dash">dashboard</Link> </p> </div> ) } const User = () => <div>User profile</div> const Dashboard = () => <div>Dashboard</div> ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div> <script src="https://unpkg.com/react@16.13.1/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@16.13.1/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/history@5.0.0/umd/history.production.min.js"></script> <script src="https://unpkg.com/react-router@6.0.0-alpha.5/umd/react-router.production.min.js"></script> <script src="https://unpkg.com/react-router-dom@6.0.0-alpha.5/umd/react-router-dom.production.min.js"></script> <script>var { BrowserRouter, Routes, Route, Link, Outlet, useNavigate, useLocation } = window.ReactRouterDOM;</script>
Alternative: Define your routes as plain JavaScript objects via useRoutes
.替代方案:通过
useRoutes
将您的路由定义为纯 JavaScript 对象。
You can use separates routes to meet requirements of larger apps like code splitting:您可以使用分隔路由来满足大型应用程序的需求,例如代码拆分:
// inside App.jsx:
<Routes>
<Route path="/*" element={<Home />} />
</Routes>
// inside Home.jsx:
<Routes>
<Route path="user" element={<User />} />
<Route path="dash" element={<Dashboard />} />
</Routes>
const App = () => { return ( <BrowserRouter> <Routes> // /js is start path of stack snippet <Route path="/js/*" element={<Home />} /> </Routes> </BrowserRouter> ); } const Home = () => { const location = useLocation() return ( <div> <p>URL path: {location.pathname}</p> <Routes> <Route path="user" element={<User />} /> <Route path="dash" element={<Dashboard />} /> </Routes> <p> <Link to="user" style={{paddingRight: "5px"}}>user</Link> <Link to="dash">dashboard</Link> </p> </div> ) } const User = () => <div>User profile</div> const Dashboard = () => <div>Dashboard</div> ReactDOM.render(<App />, document.getElementById("root"));
<div id="root"></div> <script src="https://unpkg.com/react@16.13.1/umd/react.production.min.js"></script> <script src="https://unpkg.com/react-dom@16.13.1/umd/react-dom.production.min.js"></script> <script src="https://unpkg.com/history@5.0.0/umd/history.production.min.js"></script> <script src="https://unpkg.com/react-router@6.0.0-alpha.5/umd/react-router.production.min.js"></script> <script src="https://unpkg.com/react-router-dom@6.0.0-alpha.5/umd/react-router-dom.production.min.js"></script> <script>var { BrowserRouter, Routes, Route, Link, Outlet, useNavigate, useLocation } = window.ReactRouterDOM;</script>
You can try something like Routes.js您可以尝试类似 Routes.js
import React, { Component } from 'react'
import { BrowserRouter as Router, Route } from 'react-router-dom';
import FrontPage from './FrontPage';
import Dashboard from './Dashboard';
import AboutPage from './AboutPage';
import Backend from './Backend';
import Homepage from './Homepage';
import UserPage from './UserPage';
class Routes extends Component {
render() {
return (
<div>
<Route exact path="/" component={FrontPage} />
<Route exact path="/home" component={Homepage} />
<Route exact path="/about" component={AboutPage} />
<Route exact path="/admin" component={Backend} />
<Route exact path="/admin/home" component={Dashboard} />
<Route exact path="/users" component={UserPage} />
</div>
)
}
}
export default Routes
App.js应用程序.js
import React, { Component } from 'react';
import logo from './logo.svg';
import './App.css';
import { BrowserRouter as Router, Route } from 'react-router-dom'
import Routes from './Routes';
class App extends Component {
render() {
return (
<div className="App">
<Router>
<Routes/>
</Router>
</div>
);
}
}
export default App;
I think you can achieve the same from here also.我认为你也可以从这里实现同样的目标。
A complete answer for React Router v5. React Router v5 的完整答案。
const Router = () => {
return (
<Switch>
<Route path={"/"} component={LandingPage} exact />
<Route path={"/games"} component={Games} />
<Route path={"/game-details/:id"} component={GameDetails} />
<Route
path={"/dashboard"}
render={({ match: { path } }) => (
<Dashboard>
<Switch>
<Route
exact
path={path + "/"}
component={DashboardDefaultContent}
/>
<Route path={`${path}/inbox`} component={Inbox} />
<Route
path={`${path}/settings-and-privacy`}
component={SettingsAndPrivacy}
/>
<Redirect exact from={path + "/*"} to={path} />
</Switch>
</Dashboard>
)}
/>
<Route path="/not-found" component={NotFound} />
<Redirect exact from={"*"} to={"/not-found"} />
</Switch>
);
};
export default Router;
const Dashboard = ({ children }) => {
return (
<Grid
container
direction="row"
justify="flex-start"
alignItems="flex-start"
>
<DashboardSidebarNavigation />
{children}
</Grid>
);
};
export default Dashboard;
Github repo is here. Github 仓库在这里。 https://github.com/webmasterdevlin/react-router-5-demo
https://github.com/webmasterdevlin/react-router-5-demo
I prefer to use react function.我更喜欢使用反应功能。 This solution is short and more readable
该解决方案简短且更具可读性
const MainAppRoutes = () => (
<Switch>
<Route exact path='/' component={HomePage} />
{AdminRoute()}
{SampleRoute("/sample_admin")}
</Switch>
);
/*first implementation: without params*/
const AdminRoute = () => ([
<Route path='/admin/home' component={AdminHome} />,
<Route path='/admin/about' component={AdminAbout} />
]);
/*second implementation: with params*/
const SampleRoute = (main) => ([
<Route path={`${main}`} component={MainPage} />,
<Route path={`${main}/:id`} component={MainPage} />
]);
**This code worked for me with v6**
index.js index.js
ReactDOM.render(
<React.StrictMode>
<BrowserRouter>
<Routes>
<Route path="/" element={<App />}>
<Route path="login" element={<Login />} />
<Route path="home" element={<Home />} />
</Route>
</Routes>
</BrowserRouter>
</React.StrictMode>,
document.getElementById('root')
);
App.js:应用程序.js:
function App(props) {
useEffect(() => {
console.log('reloaded');
// Checking, if Parent component re-rendering or not *it should not be, in the sense of performance*, this code doesn't re-render parent component while loading children
});
return (
<div className="App">
<Link to="login">Login</Link>
<Link to="home">Home</Link>
<Outlet /> // This line is important, otherwise we will be shown with empty component
</div>
);
}
login.js:登录.js:
const Login = () => {
return (
<div>
Login Component
</div>
)
};
home.js:主页.js:
const Home= () => {
return (
<div>
Home Component
</div>
)
};
在这里我已经在TSX中创建了一个示例,如果有人想要摆脱子路径路径中包装路由的前缀: https : //stackoverflow.com/a/47891060/5517306
interface IDefaultLayoutProps {
children: React.ReactNode
}
const DefaultLayout: React.SFC<IDefaultLayoutProps> = ({children}) => {
return (
<div className="DefaultLayout">
{children}
</div>
);
}
const LayoutRoute: React.SFC<IDefaultLayoutRouteProps & RouteProps> = ({component: Component, layout: Layout, ...rest}) => {
const handleRender = (matchProps: RouteComponentProps<{}, StaticContext>) => (
<Layout>
<Component {...matchProps} />
</Layout>
);
return (
<Route {...rest} render={handleRender}/>
);
}
const ScreenRouter = () => (
<BrowserRouter>
<div>
<Link to="/">Home</Link>
<Link to="/counter">Counter</Link>
<Switch>
<LayoutRoute path="/" exact={true} layout={DefaultLayout} component={HomeScreen} />
<LayoutRoute path="/counter" layout={DashboardLayout} component={CounterScreen} />
</Switch>
</div>
</BrowserRouter>
);
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.