我正在将反应路由器添加到现有项目中。
目前,模型被传递给根组件,该组件包含子导航的导航组件和主要组件。
反应路由器的例子我发现只有一个子组件,多个子组件改变的最佳方法是什么,而不重复两者中的布局代码?
答案 0 :(得分:106)
如果我理解正确,为了实现这一目标,您需要在Route
中定义多个组件。您可以像使用它一样使用它:
// think of it outside the context of the router, if you had pluggable
// portions of your `render`, you might do it like this
<App children={{main: <Users/>, sidebar: <UsersSidebar/>}}/>
// So with the router it looks like this:
const routes = (
<Route component={App}>
<Route path="groups" components={{main: Groups, sidebar: GroupsSidebar}}/>
<Route path="users" components={{main: Users, sidebar: UsersSidebar}}>
<Route path="users/:userId" component={Profile}/>
</Route>
</Route>
)
class App extends React.Component {
render () {
const { main, sidebar } = this.props;
return (
<div>
<div className="Main">
{main}
</div>
<div className="Sidebar">
{sidebar}
</div>
</div>
)
}
}
class Users extends React.Component {
render () {
return (
<div>
{/* if at "/users/123" `children` will be <Profile> */}
{/* UsersSidebar will also get <Profile> as this.props.children,
so its a little weird, but you can decide which one wants
to continue with the nesting */}
{this.props.children}
</div>
)
}
}
同时查看sidebar example app,可以为您提供更多帮助。
修改强> 根据@ Luiz的评论:
在最新版本的路由器(v3)中,组件位于props对象的根目录
所以:
const { main, sidebar } = this.props.children;
变为:
const { main, sidebar } = this.props;
修改强> 在react-router v4中,这可以完成(如new docs中提供的示例):
import React from 'react'
import {
BrowserRouter as Router,
Route,
Link
} from 'react-router-dom'
// Each logical "route" has two components, one for
// the sidebar and one for the main area. We want to
// render both of them in different places when the
// path matches the current URL.
const routes = [
{ path: '/',
exact: true,
sidebar: () => <div>home!</div>,
main: () => <h2>Home</h2>
},
{ path: '/bubblegum',
sidebar: () => <div>bubblegum!</div>,
main: () => <h2>Bubblegum</h2>
},
{ path: '/shoelaces',
sidebar: () => <div>shoelaces!</div>,
main: () => <h2>Shoelaces</h2>
}
]
const SidebarExample = () => (
<Router>
<div style={{ display: 'flex' }}>
<div style={{
padding: '10px',
width: '40%',
background: '#f0f0f0'
}}>
<ul style={{ listStyleType: 'none', padding: 0 }}>
<li><Link to="/">Home</Link></li>
<li><Link to="/bubblegum">Bubblegum</Link></li>
<li><Link to="/shoelaces">Shoelaces</Link></li>
</ul>
{routes.map((route, index) => (
// You can render a <Route> in as many places
// as you want in your app. It will render along
// with any other <Route>s that also match the URL.
// So, a sidebar or breadcrumbs or anything else
// that requires you to render multiple things
// in multiple places at the same URL is nothing
// more than multiple <Route>s.
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.sidebar}
/>
))}
</div>
<div style={{ flex: 1, padding: '10px' }}>
{routes.map((route, index) => (
// Render more <Route>s with the same paths as
// above, but different components this time.
<Route
key={index}
path={route.path}
exact={route.exact}
component={route.main}
/>
))}
</div>
</div>
</Router>
)
export default SidebarExample
请务必在此处查看新的React Router v4文档:https://reacttraining.com/react-router/
答案 1 :(得分:9)
组件可以是返回JSX的函数。
<Route>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="Invite" component={()=>(<div><Home/><Invite/></div>)} />
</Route>
</Route>
答案 2 :(得分:3)
做到这一点的简单,干净的方法是避免滥用重新渲染(在React Router v5上进行了测试,需要在React Router v4上进行确认):
$dates=array();
foreach ($data as $key => $row) {
$dates[$key] = $row['post_date'];
}
array_multisort($dates , SORT_NUMERIC, $yourArray);
可以重构为:
<Switch>
<Route exact path={["/route1/:id/:token", "/"]}>
<Layout1>
<Route path="/route1/:id/:token" component={SetPassword} />
<Route exact path="/" component={SignIn} />
</Layout1>
</Route>
<Route path={["/route2"]}>
<Layout2>
<Route path="/route2" component={Home} />
</Layout2>
</Route>
</Switch>
具有:
const routes = [
{
layout:Layout1,
subRoutes:[
{
path:"/route1/:id/:token",
component:SetPassword
},
{
exact:true,
path:"/",
component:SignIn
},
]
},
{
layout:Layout2,
subRoutes:[
{
path:"/route2",
component:Home
},
]
}
];
答案 3 :(得分:1)
要补充塞巴斯蒂安的答案,这似乎对我有用,包括未找到的路线和动态子路线。下面的示例使我的LayoutAuthenticated
和LayoutAnonymous
仅渲染一次,而不是在使用相同布局的路由中的每条路线更改上渲染一次。还添加了PageSettings
示例,以显示此体系结构内的嵌套路由。希望这对其他人有帮助!
(示例包括TypeScript)
const publicRoutes = [
{
key: "login",
path: "/login",
component: PageLogin,
exact: true
},
{
key: "signup",
path: "/signup",
component: PageSignup,
exact: true
},
{
key: "forgot-password",
path: "/forgot-password",
component: PageForgotPassword,
exact: true
}
];
const privateRoutes = [
{
key: "home",
path: "/",
component: PageHome,
exact: true
},
{
key: "settings",
path: "/settings",
component: PageSettings, // sub routing is handled in that component
exact: false // important, PageSettings is just a new Router switch container
}
];
// Routes.tsx
<Router>
<Switch>
<Route exact path={["/", "/settings", "/settings/*"]}>
<LayoutAuthenticated>
<Switch>
{privateRoutes.map(privateRouteProps => (
<PrivateRoute {...privateRouteProps} />
))}
</Switch>
</LayoutAuthenticated>
</Route>
<Route exact path={["/login", "/signup", "/forgot-password"]}>
<LayoutAnonymous>
<Switch>
{publicRoutes.map(publicRouteProps => (
<PublicRoute {...publicRouteProps} />
))}
</Switch>
</LayoutAnonymous>
</Route>
<Route path="*">
<LayoutAnonymous>
<Switch>
<Route component={PageNotFound} />
</Switch>
</LayoutAnonymous>
</Route>
</Switch>
</Router>
// LayoutAnonymous.tsx
import React from 'react';
export const LayoutAnonymous: React.FC<{}> = props => {
return (
<div>
{props.children}
</div>
)
}
// LayoutAuthenticated.tsx
import React from 'react';
import { MainNavBar } from '../components/MainNavBar';
import { MainContent } from '../components/MainContent';
export const LayoutAuthenticated: React.FC<{}> = props => {
return (
<>
<MainNavBar />
<MainContent>
{props.children}
</MainContent>
</>
)
}
// PrivateRoute.tsx
import React from "react";
import {
Route,
Redirect,
RouteProps
} from "react-router-dom";
import { useSelector } from "react-redux";
interface Props extends RouteProps {}
export const PrivateRoute: React.FC<Props> = props => {
const isAuthenticated: boolean = useSelector<any, any>((stores) => stores.auth.isAuthenticated);
const { component: Component, ...restProps } = props;
if (!Component) return null;
return (
<Route
{...restProps}
render={routeRenderProps =>
isAuthenticated ? (
<Component {...routeRenderProps} />
) : (
<Redirect
to={{
pathname: "/login",
state: { from: routeRenderProps.location }
}}
/>
)
}
/>
)
}
// PublicRoute.tsx
import React from "react";
import { Route, RouteProps, Redirect } from "react-router-dom";
import { useSelector } from "react-redux";
interface Props extends RouteProps {}
export const PublicRoute: React.FC<Props> = props => {
const isAuthenticated: boolean = useSelector<any, any>((stores) => stores.auth.isAuthenticated);
const { component: Component, ...restProps } = props;
if (!Component) return null;
return (
<Route
{...restProps}
render={routeRenderProps => (
!isAuthenticated ? (
<Component {...routeRenderProps} />
) : (
<Redirect
to={{
pathname: "/",
state: { from: routeRenderProps.location }
}}
/>
)
)}
/>
)
}
// PageSettings.tsx
import React from "react";
import { LinkContainer } from "react-router-bootstrap";
import Button from "react-bootstrap/Button";
import {
Switch,
useRouteMatch,
Redirect,
Switch
} from "react-router-dom";
import { PrivateRoute } from "../../routes/PrivateRoute";
import { PageSettingsProfile } from "./profile";
import { PageSettingsBilling } from "./billing";
import { PageSettingsAccount } from "./account";
export const PageSettings = () => {
const { path } = useRouteMatch();
return (
<div>
<h2>Settings</h2>
<Redirect strict from={path} to={`${path}/profile`} />
<LinkContainer to={`${path}/profile`}>
<Button>Profile</Button>
</LinkContainer>
<LinkContainer to={`${path}/billing`}>
<Button>Billing</Button>
</LinkContainer>
<LinkContainer to={`${path}/account`}>
<Button>Account</Button>
</LinkContainer>
<Switch>
<PrivateRoute path={`${path}/profile`} component={PageSettingsProfile} />
<PrivateRoute path={`${path}/billing`} component={PageSettingsBilling} />
<PrivateRoute path={`${path}/account`} component={PageSettingsAccount} />
</Switch>
</div>
);
};
答案 4 :(得分:0)
你可以直接在 Router 标签内使用两个 switch 语句,而不是那么多的 Cluttering。`
<div className= {classes.root}>
<CssBaseline></CssBaseline>
<Router>
<Switch>
<Route path="/" exact component={Header}></Route>
<Route path="/login" component={Login}></Route>
</Switch>
<Switch>
<Route path="/" exact component={Checkout}></Route>
</Switch>
</Router>
</div>