Mise à niveau avec React: React Router

Ce tutoriel est le premier d’une série en trois parties sur React de Brad Westfall. Lorsque Brad m’a présenté cela, il a souligné qu’il y avait une bonne quantité de tutoriels sur la façon de démarrer dans React, mais pas autant sur l’endroit où aller à partir de là. Si vous êtes tout nouveau à React, je vous recommande de regarder cette vidéo d’introduction. Cette série reprend là où les bases s’arrêtent.

Série d’articles:

  1. React Router (Vous êtes ici!)
  2. Composants de conteneur
  3. Redux

Attention ! Cet article a été écrit pre-React Router 4, qui est devenu un choix plus standard pour le routage dans React. Il y a un nouvel article couvrant React Router 4 ici que vous devriez certainement lire.

Lorsque j’apprenais pour la première fois, j’ai trouvé de nombreux guides pour débutants (c’est-à-dire 1, 2, 3, 4) qui montraient comment créer des composants uniques et les rendre dans le DOM. Ils ont bien fait d’enseigner les bases comme JSX et les accessoires, mais j’ai eu du mal à comprendre comment React fonctionne dans une image plus grande – comme une application de page unique (SPA) du monde réel. Comme cette série couvre beaucoup de matériel, elle ne couvrira pas les concepts de débutant absolu. Au lieu de cela, cela commencera par l’hypothèse que vous comprenez déjà comment créer et rendre au moins un composant.

Pour ce que ça vaut, voici quelques autres grands guides qui s’adressent aux débutants:

  • Réagissez.js et Comment S’Intègre-T-Il À Tout Le Reste?
  • Repenser les meilleures pratiques (de l’industrie)
  • React.Introduction js Pour Les Personnes Qui Connaissent Juste Assez de jQuery Pour s’en sortir

Code de la série

Cette série est également livrée avec du code avec lequel jouer sur GitHub. Tout au long de la série, nous construirons un SPA de base axé sur les utilisateurs et les widgets.

Pour garder les choses simples et brèves, les exemples de cette série commenceront par supposer que React et React Router sont récupérés à partir d’un CDN. Vous ne verrez donc pas require() ou import dans les exemples immédiats ci-dessous. Vers la fin de ce tutoriel, nous présenterons Webpack et Babel pour les guides GitHub. À ce moment-là, tout est ES6!

React-Router

React n’est pas un framework, c’est une bibliothèque. Par conséquent, cela ne résout pas tous les besoins d’une application. Il fait un excellent travail pour créer des composants et fournir un système de gestion de l’état, mais la création d’un SPA plus complexe nécessitera une fonte de support. Le premier que nous allons examiner est React Router.

Si vous avez déjà utilisé un routeur frontal, beaucoup de ces concepts vous seront familiers. Mais contrairement à tout autre routeur que j’ai utilisé auparavant, React Router utilise JSX, ce qui peut sembler un peu étrange au début.

En tant qu’amorce, voici à quoi ressemble le rendu d’un seul composant:

var Home = React.createClass({ render: function() { return (<h1>Welcome to the Home Page</h1>); }});ReactDOM.render(( <Home />), document.getElementById('root'));

Voici comment le composant Home serait rendu avec React Router:

...ReactDOM.render(( <Router> <Route path="/" component={Home} /> </Router>), document.getElementById('root'));

Notez que <Router> et <Route> sont deux choses différentes. Ce sont techniquement des composants React, mais ils ne créent pas eux-mêmes de DOM. Bien qu’il puisse sembler que le <Router> lui-même est rendu au 'root', nous ne faisons que définir des règles sur le fonctionnement de notre application. À l’avenir, vous verrez souvent ce concept: les composants existent parfois non pas pour créer eux-mêmes des DOM, mais pour coordonner d’autres composants qui le font.

Dans l’exemple, le <Route> définit une règle selon laquelle la visite de la page d’accueil / rendra le composant Home dans le 'root'.

Plusieurs routes

Dans l’exemple précédent, l’itinéraire unique est très simple. Cela ne nous donne pas beaucoup de valeur car nous avions déjà la possibilité de rendre le composant Home sans que le routeur soit impliqué.

La puissance du routeur React intervient lorsque nous utilisons plusieurs routes pour définir le composant à restituer en fonction du chemin actuellement actif:

ReactDOM.render(( <Router> <Route path="/" component={Home} /> <Route path="/users" component={Users} /> <Route path="/widgets" component={Widgets} /> </Router>), document.getElementById('root'));

Chaque <Route> rendra son composant respectif lorsque son chemin correspond à l’URL. Un seul de ces trois composants sera rendu dans le 'root' à un moment donné. Avec cette stratégie, nous montons le routeur sur le DOM 'root' une fois, puis le routeur échange les composants avec les changements de route.

Il est également intéressant de noter que le routeur commutera des routes sans faire de demandes au serveur, alors imaginez que chaque composant puisse être une toute nouvelle page.

Mise en page réutilisable

Nous commençons à voir les débuts modestes d’une application à une seule Page. Cependant, cela ne résout toujours pas les problèmes réels. Bien sûr, nous pourrions construire les trois composants pour être des pages HTML complètes, mais qu’en est-il de la réutilisation du code? Il y a de fortes chances que ces trois composants partagent des actifs communs comme un en-tête et une barre latérale, alors comment empêcher la répétition HTML dans chaque composant?

Imaginons que nous construisions une application Web qui ressemblait à cette maquette:

Une maquette de site web simple.

Lorsque vous commencez à réfléchir à la façon dont cette maquette peut être décomposée en sections réutilisables, vous pourriez vous retrouver avec cette idée:

Comment vous pourriez diviser la maquette Web simple en sections.

Penser en termes de composants et d’agencements emboîtables nous permettra de créer des pièces réutilisables.

Soudain, le département artistique vous fait savoir que l’application a besoin d’une page pour rechercher des widgets qui ressemble à la page pour rechercher des utilisateurs. La Liste des utilisateurs et la liste des widgets nécessitant toutes deux le même « look » pour leur page de recherche, l’idée d’avoir la mise en page de la recherche en tant que composant séparé a encore plus de sens maintenant:

Recherchez des widgets maintenant, à la place des utilisateurs, mais les sections parentes restent les mêmes.

La mise en page de recherche peut maintenant être un modèle parent pour toutes sortes de pages de recherche. Et alors que certaines pages peuvent avoir besoin d’une mise en page de recherche, d’autres peuvent directement utiliser la mise en page principale sans elle:

Une mise en page découplée.

C’est une stratégie courante et si vous avez utilisé un système de modèles, vous avez probablement fait quelque chose de très similaire. Maintenant, travaillons sur le HTML. Pour commencer, nous ferons du HTML statique sans considérer JavaScript:

<div> <!-- Main Layout --> <div class="app"> <header class="primary-header"><header> <aside class="primary-aside"></aside> <main> <!-- Search Layout --> <div class="search"> <header class="search-header"></header> <div class="results"> <!-- User List --> <ul class="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> </div> <div class="search-footer pagination"></div> </div> </main> </div></div>

N’oubliez pas que l’élément 'root' sera toujours présent car c’est le seul élément que possède le corps HTML initial avant le démarrage de JavaScript. Le mot « root » est approprié car toute notre application React y sera montée. Mais il n’y a pas de « bon nom » ou de convention à ce que vous appelez ça. J’ai choisi « root », nous continuerons donc à l’utiliser tout au long des exemples. Attention, il est fortement déconseillé de monter directement sur l’élément <body>.

Après avoir créé le code HTML statique, convertissez-le en composants React:

var MainLayout = React.createClass({ render: function() { // Note the `className` rather than `class` // `class` is a reserved word in JavaScript, so JSX uses `className` // Ultimately, it will render with a `class` in the DOM return ( <div className="app"> <header className="primary-header"><header> <aside className="primary-aside"></aside> <main> {this.props.children} </main> </div> ); }});var SearchLayout = React.createClass({ render: function() { return ( <div className="search"> <header className="search-header"></header> <div className="results"> {this.props.children} </div> <div className="search-footer pagination"></div> </div> ); }});var UserList = React.createClass({ render: function() { return ( <ul className="user-list"> <li>Dan</li> <li>Ryan</li> <li>Michael</li> </ul> ); }});

Ne soyez pas trop distrait entre ce que j’appelle « Mise en page » et « Composant ». Tous les trois sont des composants React. Je choisis simplement d’appeler deux d’entre eux des « mises en page » car c’est le rôle qu’ils jouent.

Nous utiliserons éventuellement des « routes imbriquées » pour placer UserList à l’intérieur de SearchLayout, puis à l’intérieur de MainLayout. Mais d’abord, notez que lorsque UserList est placé dans son parent SearchLayout, le parent utilisera this.props.children pour déterminer son emplacement. Tous les composants ont this.props.children comme prop, mais ce n’est que lorsque les composants sont imbriqués que le composant parent remplit automatiquement cette prop par React. Pour les composants qui ne sont pas des composants parents, this.props.children sera null.

Routes imbriquées

Alors, comment pouvons-nous imbriquer ces composants ? Le routeur le fait pour nous lorsque nous imbriquons des routes:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route component={SearchLayout}> <Route path="users" component={UserList} /> </Route> </Route> </Router>), document.getElementById('root'));

Les composants seront imbriqués en fonction de la façon dont le routeur imbrique ses routes. Lorsque l’utilisateur visite la route /users, React Router place le composant UserList à l’intérieur de SearchLayout, puis les deux à l’intérieur de MainLayout. Le résultat final de la visite de /users sera les trois composants imbriqués placés à l’intérieur de 'root'.

Notez que nous n’avons pas de règle pour le moment où l’utilisateur visite le chemin de la page d’accueil (/) ou souhaite rechercher des widgets. Ceux-ci ont été laissés de côté pour la simplicité, mais mettons-les avec le nouveau routeur:

ReactDOM.render(( <Router> <Route component={MainLayout}> <Route path="/" component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

Vous avez probablement remarqué maintenant que JSX suit des règles XML dans le sens où le composant Route peut être écrit en une seule balise : <Route /> ou deux : <Route>...</Route>. Cela est vrai pour tous les JSX, y compris vos composants personnalisés et vos nœuds DOM normaux. Par exemple, <div /> est JSX valide et sera converti en <div></div> lors du rendu.

Pour plus de concision, imaginez que WidgetList ressemble au UserList.

Puisque <Route component={SearchLayout}> a maintenant deux routes enfants, l’utilisateur peut visiter /users ou /widgets et le <Route> correspondant chargera ses composants respectifs à l’intérieur du composant SearchLayout.

Notez également comment le composant Home sera placé directement à l’intérieur de MainLayout sans que SearchLayout ne soit impliqué — en raison de la façon dont les <Route> sont imbriqués. Vous pouvez probablement imaginer qu’il est facile de réorganiser la façon dont les mises en page et les composants sont imbriqués en réorganisant les itinéraires.

IndexRoutes

Le routeur React est très expressif et il y a souvent plus d’une façon de faire la même chose. Par exemple, nous aurions également pu écrire le routeur ci-dessus comme ceci:

ReactDOM.render(( <Router> <Route path="/" component={MainLayout}> <IndexRoute component={Home} /> <Route component={SearchLayout}> <Route path="users" component={UserList} /> <Route path="widgets" component={WidgetList} /> </Route> </Route> </Router>), document.getElementById('root'));

Malgré son aspect différent, ils fonctionnent tous les deux exactement de la même manière.

Attributs de route optionnels

Parfois, <Route> aura un attribut component sans path, comme dans la route SearchLayout ci-dessus. D’autres fois, il peut être nécessaire d’avoir un <Route> avec un path et aucun component. Pour voir pourquoi, commençons par cet exemple:

<Route path="product/settings" component={ProductSettings} /><Route path="product/inventory" component={ProductInventory} /><Route path="product/orders" component={ProductOrders} />

La partie /product du path est répétitive. Nous pouvons supprimer la répétition en enveloppant les trois routes dans une nouvelle <Route>:

<Route path="product"> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

Encore une fois, React Router montre son expressivité. Quiz: avez-vous remarqué le problème avec les deux solutions? Pour le moment, nous n’avons pas de règles pour le moment où l’utilisateur visite le chemin /product.

Pour résoudre ce problème, nous pouvons ajouter un IndexRoute:

<Route path="product"> <IndexRoute component={ProductProfile} /> <Route path="settings" component={ProductSettings} /> <Route path="inventory" component={ProductInventory} /> <Route path="orders" component={ProductOrders} /></Route>

Utilisez <Link> et non <a>

Lors de la création d’ancres pour vos routes, vous devrez utiliser <Link to=""> au lieu de <a href="">. Ne vous inquiétez pas cependant, lorsque vous utilisez le composant <Link>, React Router vous donnera finalement une ancre ordinaire dans le DOM. L’utilisation de <Link> est cependant nécessaire pour que React Router fasse une partie de sa magie de routage.

Ajoutons un lien (ancres) à notre MainLayout:Les attributs

var MainLayout = React.createClass({ render: function() { return ( <div className="app"> <header className="primary-header"></header> <aside className="primary-aside"> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/users">Users</Link></li> <li><Link to="/widgets">Widgets</Link></li> </ul> </aside> <main> {this.props.children} </main> </div> ); }});

des composants <Link> seront transmis à l’ancre qu’ils créent. Donc, ce JSX:

<Link to="/users" className="users">

Deviendra cela dans DOM:

<a href="/users" class="users">

Si vous devez créer une ancre pour des chemins non-routeurs, tels qu’un site Web extérieur, utilisez des balises d’ancrage normales comme d’habitude. Pour plus d’informations, consultez la documentation pour IndexRoute et Link.

Liens actifs

Une caractéristique intéressante du composant <Link> est sa capacité à savoir quand il est actif:

<Link to="/users" activeClassName="active">Users</Link>

Si l’utilisateur est sur le chemin /users, le routeur recherchera les ancres correspondantes qui ont été faites avec <Link> et il basculera leur classe active. En savoir plus sur cette fonctionnalité.

Historique du navigateur

Pour éviter toute confusion, j’ai laissé de côté un détail important jusqu’à présent. Le <Router> doit savoir quelle stratégie de suivi de l’historique utiliser. Les documents React Router recommandent browserHistory qui est implémenté comme suit:

var browserHistory = ReactRouter.browserHistory;ReactDOM.render(( <Router history={browserHistory}> ... </Router>), document.getElementById('root'));

Dans les versions précédentes de React Router, l’attribut history n’était pas requis et la valeur par défaut était d’utiliser hashHistory. Comme son nom l’indique, il utilisait un signe de hachage # dans l’URL pour gérer le routage de style SPA frontal, similaire à ce que vous pourriez attendre d’un Backbone.routeur js.

Avec hashHistory, les URL ressembleront à ceci:

  • example.com
  • example.com/#/users?_k=ckuvup
  • example.com/#/widgets?_k=ckuvup

Qu’est-ce qui se passe avec ces chaînes de requête laides ?

Lorsque browserHistory est implémenté, les chemins semblent plus organiques:

  • example.com
  • example.com/users Exemple
  • .com /widgets

Il y a cependant une mise en garde sur le serveur lorsque browserHistory est utilisé sur le front-end. Si l’utilisateur commence sa visite à example.com puis navigue vers /users et /widgets, React Router gère ce scénario comme prévu. Cependant, si l’utilisateur commence sa visite en tapant example.com/widgets directement dans le navigateur, ou s’il actualise example.com/widgets, le navigateur doit alors faire au moins une demande au serveur pour /widgets. S’il n’y a pas de routeur côté serveur, cela fournira un 404:

Attention aux URL. Vous aurez besoin d’un routeur côté serveur.

Pour résoudre le problème 404 du serveur, React Router recommande un routeur générique côté serveur. Avec cette stratégie, quel que soit l’itinéraire côté serveur appelé, le serveur doit toujours servir le même fichier HTML. Ensuite, si l’utilisateur démarre directement à example.com/widgets, même si le même fichier HTML est renvoyé, React Router est suffisamment intelligent pour charger le composant correct.

L’utilisateur ne remarquera rien de bizarre, mais vous pourriez avoir des soucis à toujours servir le même fichier HTML. Dans les exemples de code, cette série continuera à utiliser la stratégie « routeur générique », mais c’est à vous de gérer votre routage côté serveur de la manière qui vous convient.

Le routeur React peut-il être utilisé à la fois côté serveur et côté client de manière isomorphe ? Bien sûr, cela peut, mais cela dépasse largement le cadre de ce tutoriel.

Redirection avec browserHistory

L’objet browserHistory est un singleton, vous pouvez donc l’inclure dans n’importe lequel de vos fichiers. Si vous devez rediriger manuellement l’utilisateur dans l’un de vos codes, vous pouvez utiliser sa méthode push pour le faire:

browserHistory.push('/some/path');

Correspondance d’itinéraire

Le routeur React gère la correspondance d’itinéraire de la même manière que les autres routeurs:

<Route path="users/:userId" component={UserProfile} />

Cette route correspondra lorsque l’utilisateur visite un chemin commençant par users/ et ayant une valeur par la suite. Il correspondra à /users/1, /users/143 ou même /users/abc (que vous devrez valider par vous-même).

Le routeur React transmettra la valeur de :userId en tant qu’accessoire au UserProfile. Ces accessoires sont accessibles en tant que this.props.params.userId à l’intérieur de UserProfile.

Démo du routeur

À ce stade, nous avons suffisamment de code pour afficher une démo.

Voir la démo Pen React-Router de Brad Westfall (@bradwestfall) sur CodePen.

Si vous avez cliqué sur quelques routes dans l’exemple, vous remarquerez peut-être que les boutons Précédent et suivant du navigateur fonctionnent avec le routeur. C’est l’une des principales raisons pour lesquelles ces stratégies history existent. De plus, gardez à l’esprit qu’à chaque itinéraire que vous visitez, aucune demande n’est faite au serveur, sauf la toute première pour obtenir le code HTML initial. C’est cool, ça ?

ES6

Dans notre exemple CodePen, React, ReactDOM et ReactRouter sont des variables globales d’un CDN. À l’intérieur de l’objet ReactRouter se trouvent toutes sortes de choses dont nous avons besoin comme les composants Router et Route. Nous pourrions donc utiliser ReactRouter comme ceci:

ReactDOM.render(( <ReactRouter.Router> <ReactRouter.Route ... /> </ReactRouter.Router>), document.getElementById('root'));

Ici, nous devons préfixer tous nos composants de routeur avec leur objet parent ReactRouter. Ou nous pourrions utiliser la nouvelle syntaxe de déstructuration d’ES6 comme ceci:

var { Router, Route, IndexRoute, Link } = ReactRouter

Cela « extrait » des parties de ReactRouter en variables normales afin que nous puissions y accéder directement.

À partir de maintenant, les exemples de cette série utiliseront une variété de syntaxes ES6, y compris la déstructuration, l’opérateur de propagation, les importations / exportations, et peut-être d’autres. Il y aura une brève explication de chaque nouvelle syntaxe telle qu’elle apparaît et le dépôt GitHub fourni avec cette série contient également de nombreuses explications ES6.

Groupage avec webpack et Babel

Comme indiqué précédemment, cette série est livrée avec un dépôt GitHub afin que vous puissiez expérimenter avec le code. Comme il ressemblera à l’étoffe d’un SPA du monde réel, il utilisera des outils comme webpack et Babel.

  • webpack regroupe plusieurs fichiers JavaScript en un seul fichier pour le navigateur.
  • Babel convertira le code ES6 (ES2015) en ES5, car la plupart des navigateurs ne comprennent pas encore tout ES6. À mesure que cet article vieillit, les navigateurs prendront en charge ES6 et Babel peut ne pas être nécessaire.

Si vous n’êtes pas encore trop à l’aise avec ces outils, ne vous inquiétez pas, l’exemple de code a déjà tout configuré pour que vous puissiez vous concentrer sur React. Mais assurez-vous de revoir l’exemple de code README.md fichier pour la documentation de workflow supplémentaire.

Faites attention à la syntaxe obsolète

La recherche d’informations sur React Router sur Google peut vous amener sur l’un des nombreux articles ou pages StackOverflow qui ont été écrits lorsque React Router était dans sa version antérieure à la version 1.0. De nombreuses fonctionnalités du pré-1.les versions 0 sont maintenant obsolètes. Voici une courte liste:

  • <Route name="" /> est obsolète. Utilisez <Route path="" /> à la place.
  • <Route handler="" /> est obsolète. Utilisez <Route component="" /> à la place.
  • <NotFoundRoute /> est obsolète. Voir l’alternative
  • <RouteHandler /> est obsolète.
  • willTransitionTo est obsolète. Voir OnEnter
  • willTransitionFrom est obsolète. Voir onLeave
  • Les « Emplacements » sont maintenant appelés « historiques ».

Voir la liste complète des versions 1.0.0 et 2.0.0

Résumé

Il y a encore plus de fonctionnalités dans React Router qui n’étaient pas affichées, alors assurez-vous de consulter les documents de l’API. Les créateurs de React Router ont également créé un tutoriel étape par étape pour React Router et vérifient également ce React.vidéo js Conf sur la création du routeur React.

Un merci spécial à Lynn Fisher pour l’œuvre @lynnandtonic

Série d’articles:

  1. React Router (Vous êtes ici!)
  2. Composants de conteneur
  3. Redux

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée.