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;
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.
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.
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.
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;
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.
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
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>
);