Routes imbriquées avec react router v4

Je suis actuellement aux prises avec des routes d'imbrication utilisant react router v4.

L'exemple le plus proche était la configuration de Réagir-Routeur v4 Documentation.

Je veux diviser mon application en 2 parties différentes.

Un frontend et une zone d'administration.

Je pensais à quelque chose comme ceci:

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

Le frontend a une disposition et un style différents de ceux de la zone d'administration. Donc, dans la page d'accueil de la route, à propos et donc on devrait être l'enfant route.

/la maison doit être affiché dans le composant Frontpage et /admin/home doit être rendue dans le composant d'arrière-plan.

J'ai essayé quelques variations mais j'ai toujours fini par ne pas frapper / home ou / admin / home.

Modifier - 19.04.2017

Parce que ce post a beaucoup de vues en ce moment, je l'ai mis à jour avec la solution finale. J'espère que cela aide quelqu'un.

Modifier - 08.05.2017

Supprimé ancien solutions

Solution Finale:

C'est la solution finale que j'utilise en ce moment. Cet exemple a également un composant d'erreur globale comme une page 404 traditionnelle.

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;
157
demandé sur datoml 2017-01-05 01:15:28

7 réponses

Dans réagissent-routeur-v4 vous n'avez pas de nid <Routes />. Au lieu de cela, vous les mettez dans un autre <Component />.


Par exemple

<Route path='/topics' component={Topics}>
  <Route path='/topics/:topicId' component={Topic} />
</Route>

Devrait devenir

<Route path='/topics' component={Topics} />

Avec

const Topics = ({ match }) => (
  <div>
    <h2>Topics</h2>
    <Link to={`${match.url}/exampleTopicId`}>
      Example topic
    </Link>
    <Route path={`${match.url}/:topicId`} component={Topic}/>
  </div>
) 

Voici un exemple de base directement à partir de la réaction-routeur documentation.

198
répondu Lyubomir 2017-04-10 21:53:34

Je voulais juste mentionner react-router v4 a radicalement changé depuis que cette question a été postée/répondue.

Il n'y a plus de Composant <Match>! <Switch> est de s'assurer que seule la première correspondance est rendue. <Redirect> Eh bien .. redirige vers un autre itinéraire. Utilisez ou omettez exact pour insérer ou exclure une correspondance partielle.

Voir les documents. Ils sont grands. https://reacttraining.com/react-router/

Voici un exemple que j'espère utilisable pour répondre à votre 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>

J'espère que cela a aidé, faites-le moi savoir. Si cet exemple ne répond pas assez bien à votre question, dites-le-moi et je verrai si je peux le modifier.

36
répondu jar0m1r 2017-05-02 07:42:34

Il est vrai que pour imbriquer des Routes, vous devez les placer dans la composante enfant de la Route.

Cependant, si vous préférez une syntaxe plus 'inline' plutôt que de diviser vos Routes entre les composants, vous pouvez utiliser le render prop sur le <Route> que vous voulez imbriquer, avec un composant fonctionnel sans état en ligne, comme ceci:

<BrowserRouter>

  <Route exact path="/" component={Frontpage} />
  <Route path="/home" component={HomePage} />
  <Route path="/about" component={AboutPage} />

  <Route
    path="/admin"
    render={({ match: { url } }) => (
      <>
        <Route exact path={`${url}/`} component={Backend} />
        <Route path={`${url}/home`} component={Dashboard} />
        <Route path={`${url}/users`} component={UserPage} />
      </>
    )}
  />

</BrowserRouter>

Si vous êtes intéressé par la raison pour laquelle render devrait être utilisé à la place de component, c'est pour empêcher le composant en ligne d'être remonté sur chaque Rendu. Voir la documentation pour plus de détails.

Notez également que l'exemple ci-dessus utilise React 16 Fragments pour envelopper les Routes imbriquées, juste pour le rendre un peu plus propre. Avant React 16, Vous pouvez simplement utiliser un conteneur <div> à la place ici.

13
répondu davnicwil 2018-03-16 12:54:20

Quelque chose comme ça.

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;
4
répondu Sanjeev Shakya 2017-06-01 18:23:00

Vous pouvez essayer quelque chose comme Route.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

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;

Je pense que vous pouvez réaliser la même chose à partir d'ici aussi.

1
répondu Aniruddh Agarwal 2018-02-07 05:02:59

Ici, j'ai créé un exemple dans TSX, si quelqu'un veut se débarrasser du préfixe de la route wrapper dans le chemin de la route enfant: https://stackoverflow.com/a/47891060/5517306

0
répondu Marcin Sadowski 2017-12-19 16:35:22
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>
);
0
répondu EVGENY GLUKHOV 2018-07-19 20:40:12