web-dev-qa-db-fra.com

React Router v4 avec plusieurs dispositions

Je voudrais afficher certains de mes itinéraires dans ma mise en page publique, et quelques autres itinéraires dans ma mise en page privée, existe-t-il un moyen propre de le faire?

Exemple qui ne fonctionne évidemment pas, mais j'espère expliquer en gros ce que je recherche:

<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>

J'aimerais que la mise en page change pour certains itinéraires, comment faire avec le nouveau routeur React?

L'imbrication des routes ne fonctionne plus et me donne cette erreur:

You should not use <Route component> and <Route children> in the same route; <Route children> will be ignored

Modifier: Le fait d'avoir des présentations enveloppant des groupes entiers d'itinéraires signifie également que ces présentations ne sont rendues qu'une seule fois tant que vous restez dans le même groupe privé/public d'itinéraires. C'est un gros problème si votre mise en page doit extraire quelque chose de votre serveur par exemple, car cela se produirait à chaque changement de page si vous enveloppez chaque page avec une mise en page.

29
Florian Bienefelt

Ce que j'ai fait pour cela, c'est créer un composant simple qui ajoute une propriété supplémentaire au composant Route qui est la mise en page:

function RouteWithLayout({layout, component, ...rest}){
  return (
    <Route {...rest} render={(props) =>
      React.createElement( layout, props, React.createElement(component, props))
    }/>
  );
}

Dans votre cas, vos itinéraires ressembleraient à ceci

<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>
27
Zaptree

Si vous utilisez TypeScript et que vous souhaitez suivre cette approche de mise en page réagit alors vous pouvez déclarer votre mise en page comme ceci:

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;

Et déclarez des itinéraires comme celui-ci:

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>;
6
David Noreña

Je ne pense pas que les dispositions appartiennent aux fichiers d'itinéraire.

Gardez l'itinéraire propre, c'est-à-dire:

<Route exact path="/" component="HomePage" />

Ensuite, dans le composant HomePage, encapsulez le contenu rendu dans la mise en page de votre choix:

...
render() {
  <PublicLayout>
    <h1>Home page!</h1>
  </PublicLayout>
}

De cette façon, les itinéraires restent super propres, et vous avez un moyen facile d'afficher les itinéraires qui devraient prendre en charge les deux dispositions (404 pages par exemple).

4
cbrandolino

Je vais écrire ceci partout où cette question a été posée, désolé si vous l'avez vue ailleurs. Je ne le fais que parce que je me bats depuis un moment et je pense que cela vaut la peine de diffuser cette solution autant que possible. Assez de mots, c'est ce que j'ai fait, je pense que c'est assez explicite. Fonctionne comme un charme et il est très facile à taper.

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>;
3
Peter Kottas

J'ai essayé de répondre à Florians mais cela ne m'a pas suffi car réagir créera une instance distincte de votre mise en page pour chaque itinéraire, donc toutes les transitions de navigation comme un glissement d'onglet seront supprimées.

J'espère une solution plus élégante, mais cela a permis à mon application de fonctionner à nouveau avec la v4.

Définissez un itinéraire pointant vers un composant qui décidera de quoi envelopper l'itinéraire

<Router>
  <Route component={AppWrap} />
</Router>

Dans AppWrap, faites quelque chose comme ce qui suit

 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 pourrait peut-être être utilisé pour une représentation plus claire de cela, pas sûr.

2
Dan Banfield

2019+

Après l'avoir recherché, la manière propre et efficace (en évitant un re-rendu abusif):

    <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>

également, il peut être refactorisé, voir ma réponse complète: https://stackoverflow.com/a/57358661/343779

2
Sebastien Horin

Mise à jour: je l'ai résolu d'une autre manière, mais si vous oblige à nommer les différentes parties de votre application avec/app ou/admin par exemple.

Chacun des composants UserRoutes, AdminRoutes et PublicRoutes est essentiellement de gros composants Switch avec la disposition spécifique à sa racine.

Voici à quoi ça ressemble:

<Router>
  <Switch>
    <Route path="/app" render={props => <UserRoutes {...props} />} />
    <Route path="/admin" render={props => <AdminRoutes {...props} />} />
    <Route path="/" render={props => <PublicRoutes {...props} />} />
  </Switch>
</Router>

Vieux: Une solution serait d'utiliser l'accessoire de rendu de chaque Route, mais cela semble vraiment lourd:

<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>
1
Florian Bienefelt

L'idée est la même que celle de Zaptree mais en utilisant la syntaxe es6 et des vérifications supplémentaires afin qu'elle puisse être utilisée à la place du composant Route de react-router

Créez un nouveau composant, par exemple /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

Utilisez le composant Route créé:

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>
1
Mr. 14

Utilisez l'accessoire de rendu du composant Route, cela ne démontera pas et ne montera pas votre composant de disposition à chaque changement d'itinéraire. Plus de détails sur la façon dont cela fonctionne ici .

Supposons que vous ayez deux composants de disposition Primary.js et Secondary.js

Dans votre méthode de rendu App.js , vous retournez simplement

<BrowserRouter>
   <Switch>
     <Route path='/login' render={() => <Secondary><Login/></Secondary>} />
     <Route path='/dashboard' render={() => <Primary><Dashboard/></Primary>} />
   </Switch>
</BrowserRouter>

Pour l'affiner davantage, vous pouvez également définir un composant de présentation de composant d'ordre supérieur pour envelopper votre composant de page avec une présentation. (Pas testé)

<Route to='/login' render={() => Secondary(Login)}
0
Anand Naik B