React Router v4 с несколькими макетами
Я бы хотел отобразить некоторые из моих маршрутов в своем общедоступном макете и некоторые другие маршруты в моем личном макете, есть ли чистый способ сделать это?
Пример, который, очевидно, не работает, но я надеюсь, что примерно то, что я ищу:
<Router>
<PublicLayout>
<Switch>
<Route exact path="/" component={HomePage} />
<Route exact path="/about" component={AboutPage} />
</Switch>
</PublicLayout>
<PrivateLayout>
<Switch>
<Route exact path="/profile" component={ProfilePage} />
<Route exact path="/dashboard" component={DashboardPage} />
</Switch>
</PrivateLayout>
</Router>
Я бы хотел, чтобы макет переключался на определенные маршруты, как мне это сделать с новым маршрутизатором?
Вложенные маршруты больше не работают и дают мне эту ошибку:
You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored
Изменить: наличие макетов, охватывающих целые группы маршрутов, также означает, что эти макеты отображаются только один раз, пока вы остаетесь в одной и той же частной/общедоступной группе маршрутов. Это очень важно, если ваш макет должен что-то извлечь из вашего сервера, например, это произойдет при каждой смене страницы, если вы обернете каждую страницу макетом.
Ответы
Ответ 1
То, что я сделал для этого, - это создать простой компонент, который добавляет дополнительное свойство к компоненту Route, который является макетом:
function RouteWithLayout({layout, component, ...rest}){
return (
<Route {...rest} render={(props) =>
React.createElement( layout, props, React.createElement(component, props))
}/>
);
}
Тогда в вашем случае ваши маршруты будут выглядеть так:
<Switch>
<RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
<RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
<RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
<RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
</Switch>
Ответ 2
Если вы используете Typescript и хотите следовать этот ответный макет aproach, то вы можете объявить свой расположение:
import './Default.less';
import React from 'react';
import { Route } from "react-router-dom";
import { Sider } from './Components';
import { Notification } from 'Client/Components';
interface IDefaultProps {
component: any
path?: string;
exact?: boolean;
}
const Default: React.SFC<IDefaultProps> = (props) => {
const { component: Component, ...rest } = props;
return <Route {...rest} render={matchProps => (
<div className="defaultLayout">
<Sider />
<div className="defaultLayoutContent">
<Component {...matchProps} />
</div>
<Notification />
</div>
)} />
}
export default Default;
И объявите маршруты следующим образом:
import React from 'react';
import { Route } from 'react-router-dom';
import { DefaultLayout } from 'Client/Layout';
import { Dashboard, Matters, Schedules, Students } from './Containers';
export const routes = <div>
<DefaultLayout exact path="/" component={Dashboard} />
<DefaultLayout path="/matters" component={Matters} />
<DefaultLayout path="/schedules" component={Schedules} />
<DefaultLayout path="/students" component={Students} />
</div>;
Ответ 3
Я не думаю, что макеты принадлежат файлам маршрутов.
Сохраняйте маршрут в чистоте, т.е.
<Route exact path="/" component="HomePage" />
Затем в компоненте HomePage
оберните отображаемый контент в ваш макет выбора:
...
render() {
<PublicLayout>
<h1>Home page!</h1>
</PublicLayout>
}
Таким образом, маршруты остаются очень чистыми, плюс у вас есть простой способ отображения маршрутов, которые должны поддерживать оба макета (например, 404 страницы).
Ответ 4
Я собираюсь написать это везде, где этот вопрос был задан так жалко, если вы видели его в другом месте.
Я только делаю это, потому что я боролся какое-то время, и я думаю, что это стоит как можно больше распространять это решение.
Достаточно слов, это то, что я сделал, я думаю, что это довольно понятно. Работает как шарм, и его очень легко напечатать.
const withLayout = (LayoutComp, ComponentComp) => <LayoutComp><ComponentComp /></LayoutComp>;
const withDefaultLayout = (ComponentComp) => () => withLayout(Layout, ComponentComp);
const withEmptyLayout = (ComponentComp) => () => withLayout(EmptyLayout, ComponentComp);
export const routes = <div>
<Switch>
<Route path="/" exact render={withDefaultLayout(Home)} />
<Route path='/subscriptions' render={withDefaultLayout(SubscriptionsWrapped)} />
<Route path='/callback' render={withEmptyLayout(AuthCallback)} />
<Route path='/welcome/initial' render={withEmptyLayout(Start)} />
</Switch>
</div>;
Ответ 5
Я попробовал Флорианс ответить, но этого было недостаточно для меня, так как реакция создаст отдельный экземпляр вашего макета для каждого маршрута, поэтому любые навигационные переходы, такие как скользящая вкладка, будут убиты.
Я надеюсь на более элегантное решение, но у меня снова работает приложение с v4.
Определите один маршрут, указывающий на компонент, который решит, что обернуть маршрут в
<Router>
<Route component={AppWrap} />
</Router>
В приложении AppWrap выполните следующие действия:
var isPrivateLayout;
for (var path of listOfPrivateLayoutPaths) {
if (this.props.location.pathname == path) {
isPrivateLayout= true;
}
}
if (isPrivateLayout) {
return <PrivateLayout>
(routes)
</PrivatelyLayout>
} else {
return <PublicLayout>
(routes)
</PublicLayout>;
}
Route Config, возможно, может быть использован для более чистого представления этого, не уверен.
Ответ 6
2019+
После поиска, это чистый и эффективный способ (избегая оскорбительного повторного рендеринга):
<Route exact path={["/about", "/"]}>
< PublicLayout >
<Route exact path="/" component={HomePage} />
<Route path="/about" component={AboutPage} />
</PublicLayout >
</Route>
<Route path={["/profile", "/dashboard"]}>
< PrivateLayout >
<Route path="/profile" component={ProfilePage} />
<Route path="/dashboard" component={DashboardPage} />
</PrivateLayout >
</Route>
также, это может быть рефакторинг,
см мой полный ответ:fooobar.com/questions/125266/...
Ответ 7
Обновление: я решил это по-другому, но если вы заставляете пространство имен использовать разные части вашего приложения с помощью /app или/admin, например.
Каждый из компонентов UserRoutes, AdminRoutes и PublicRoutes - это в основном большие компоненты Switch с определенным макетом в своем корне.
Вот как это выглядит:
<Router>
<Switch>
<Route path="/app" render={props => <UserRoutes {...props} />} />
<Route path="/admin" render={props => <AdminRoutes {...props} />} />
<Route path="/" render={props => <PublicRoutes {...props} />} />
</Switch>
</Router>
Старый: одним из решений было бы использовать поддержку рендеринга каждого маршрута, но это кажется очень громоздким:
<Router>
<Switch>
<Route
path="/"
render={() => <PublicLayout><HomePage /></PublicLayout>}
/>
<Route
path="/about"
render={() => <PublicLayout><AboutPage /></PublicLayout>}
/>
<Route
path="/profile"
render={() => <PrivateLayout><ProfilePage /></PrivateLayout>}
/>
<Route
path="/dashboard"
render={() => <PrivateLayout><DashboardPage /></PrivateLayout>}
/>
</Switch>
</Router>
Ответ 8
Идея такая же, как у Zaptree, но с использованием синтаксиса es6 и добавленных проверок, чтобы его можно было использовать вместо компонента Route Route-маршрутизатора
Создайте новый компонент, скажем /src/components/Route/index.js:
import React, {Component} from 'react'
import PropTypes from 'prop-types'
import {Route as ReactRoute} from 'react-router'
class Route extends Component {
static propTypes = {
component: PropTypes.func.isRequired,
layout: PropTypes.func,
path: PropTypes.string,
exact: PropTypes.bool
}
render = () => {
const {component, layout, path, exact} = this.props
let routeComponent = props => React.createElement(component, props)
if (layout) {
routeComponent = props =>
React.createElement(layout, props, React.createElement(component, props))
}
return <ReactRoute path={path} exact={exact} render={routeComponent}/>
}
}
export default Route
Используйте созданный компонент Route:
import Route from 'components/Route/'
...
<Router history={createHistory()}>
<Switch>
<Route exact path='/' layout={PublicLayout} component={HomePage}/>
<Route exact path='/' layout={PrivateLayout} component={ProfilePage}/>
<Route path='/logins' component={Login}/>
</Switch>
</Router>
Ответ 9
Используйте опору рендеринга компонента Route, это не размонтирует и не монтирует ваш компонент макета при каждом изменении маршрута. Подробнее о том, как это работает, здесь.
Допустим, у вас есть два компонента макета: Primary.js и Secondary.js
.В своем методе рендеринга App.js вы просто возвращаете
<BrowserRouter>
<Switch>
<Route path='/login' render={() => <Secondary><Login/></Secondary>} />
<Route path='/dashboard' render={() => <Primary><Dashboard/></Primary>} />
</Switch>
</BrowserRouter>
Чтобы еще больше уточнить его, вы также можете определить компонент компоновки компонента высшего порядка, чтобы обернуть компонент страницы макетом. (Не проверено)
<Route to='/login' render={() => Secondary(Login)}
Ответ 10
@Qop верен, однако в новом React Router я заметил, что если у вас есть корневой путь внутри коммутатора в качестве первого маршрута, он всегда будет соответствовать ему и, следовательно, никогда не будет отображать ваши следующие маршруты. Вы должны поставить корневой путь в конце.
<Switch>
<RouteWithLayout layout={PublicLayout} path="/about" component={AboutPage}/>
<RouteWithLayout layout={PrivateLayout} path="/profile" component={ProfilePage}/>
<RouteWithLayout layout={PrivateLayout} path="/dashboard" component={DashboardPage}/>
<RouteWithLayout layout={PublicLayout} path="/" component={HomePage}/>
</Switch>
Ответ 11
Такая же идея с @Zaptree
Разметка
function PublicLayout(props) {
return (
<Route {...props} />
);
}
function PrivateLayout(props) {
return (
<Route {...props} />
);
}
Маршруты
<Switch>
<PublicLayout exact path="/" component={HomePage} />
<PrivateLayout path="/profile" component={ProfilePage} />
<Route path="/callback" component={NoLayoutPage} />
</Switch>
Ответ 12
Это решение будет работать.
<Router>
<Switch>
<PublicLayout>
<Route exact path="/" component={HomePage} />
<Route exact path="/about" component={AboutPage} />
</PublicLayout>
</Switch>
<Switch>
<PrivateLayout>
<Route exact path="/profile" component={ProfilePage} />
<Route exact path="/dashboard" component={DashboardPage} />
</PrivateLayout>
</Switch>
</Router>