我正在向现有项目添加react-router。
目前,一个模型被传递给一个根组件,该根组件包含一个用于子导航的导航组件和一个主组件。
我发现react-router的示例只有一个子组件,在不重复两个子组件的情况下更改多个子组件的最佳方法是什么?
我正在向现有项目添加react-router。
目前,一个模型被传递给一个根组件,该根组件包含一个用于子导航的导航组件和一个主组件。
我发现react-router的示例只有一个子组件,在不重复两个子组件的情况下更改多个子组件的最佳方法是什么?
如果我理解正确的话,要实现这一点,您将在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>
)
}
}
另请查看侧边栏示例应用程序,应该对您有更多帮助。
编辑: 根据@Luiz 的评论:
在最新版本的路由器 (v3) 中,组件位于 props 对象的根目录中
所以:
const { main, sidebar } = this.props.children;
变成:
const { main, sidebar } = this.props;
编辑: 在 react-router v4 中,这可以像(根据新文档中提供的示例)完成:
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/
做到这一点并避免滥用重新渲染的简单而干净的方法是(在react-router v5 上测试,需要在react-router v4 上确认):
<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
},
]
}
];
和:
<Switch>
{routes.map((route,i)=>
<Route key={i} exact={route.subRoutes.some(r=>r.exact)} path={route.subRoutes.map(r=>r.path)}>
<route.layout>
{route.subRoutes.map((subRoute,i)=>
<Route key={i} {...subRoute} />
)}
</route.layout>
</Route>
)}
</Switch>
添加塞巴斯蒂安的回答,这似乎对我有用,包括未找到的路线和动态子路线。下面的例子让我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>
);
};
组件可以是一个返回 JSX 的函数。
<Route>
<Route path="/" component={App}>
<IndexRoute component={Home} />
<Route path="Invite" component={()=>(<div><Home/><Invite/></div>)} />
</Route>
</Route>
您可以直接在 Router 标签内使用两个 switch 语句,而不是太多的混乱。`
<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>