Subir de nivel con React: Router React

Este tutorial es el primero de una serie de tres partes sobre React de Brad Westfall. Cuando Brad me lanzó esto, señaló que hay una buena cantidad de tutoriales sobre cómo comenzar en React, pero no tanto sobre a dónde ir a partir de ahí. Si eres nuevo en React, te recomiendo ver este video introductorio. Esta serie continúa donde lo básico termina.

Serie de artículos:

  1. Router React (¡Estás aquí!)
  2. Componentes de contenedores
  3. Redux

¡Advertencia! Este artículo fue escrito antes de React Router 4, que se ha convertido en una opción más estándar para enrutar en React. Hay un nuevo artículo que cubre React Router 4 aquí que definitivamente deberías leer.

Cuando estaba aprendiendo por primera vez, encontré muchas guías para principiantes (por ejemplo, 1, 2, 3, 4) que mostraban cómo hacer componentes individuales y renderizarlos en el DOM. Hicieron un buen trabajo enseñando los conceptos básicos como JSX y accesorios, pero tuve problemas para descubrir cómo funciona React en el panorama general, como una Aplicación de una sola página (SPA) del mundo real. Dado que esta serie cubre una gran cantidad de material, no cubrirá los conceptos absolutos para principiantes. En su lugar, comenzará con la suposición de que ya entiende cómo crear y renderizar al menos un componente.

Por si sirve de algo, aquí hay algunas otras excelentes guías que apuntan a principiantes:

  • Reacciona.js y Cómo Encaja Con Todo lo Demás?
  • Repensar las mejores Prácticas (de la Industria)
  • React.introducción A js Para Personas Que Saben lo suficiente de jQuery Para Pasar

Código de Serie

Esta serie también viene con código para jugar en GitHub. A lo largo de la serie, construiremos un SPA básico centrado en los usuarios y los widgets.

Para mantener las cosas simples y breves, los ejemplos de esta serie comenzarán asumiendo que React y React Router se recuperan de una CDN. Por lo tanto, no verá require() o import en los ejemplos inmediatos a continuación. Sin embargo, hacia el final de este tutorial, presentaremos Webpack y Babel para las guías de GitHub. En ese momento, ¡todo es6!

React-Router

React no es un framework, es una biblioteca. Por lo tanto, no resuelve todas las necesidades de una aplicación. Hace un gran trabajo en la creación de componentes y proporciona un sistema para administrar el estado, pero la creación de un SPA más complejo requerirá un elenco de apoyo. El primero que veremos es React Router.

Si ha utilizado cualquier enrutador front-end antes, muchos de estos conceptos le resultarán familiares. Pero a diferencia de cualquier otro enrutador que haya usado antes, React Router usa JSX, lo que podría parecer un poco extraño al principio.

Como primer, esto es lo que se siente al renderizar un solo componente:

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

Así es como se renderizaría el componente Home con React Router:

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

Tenga en cuenta que <Router> y <Route> son dos cosas diferentes. Son componentes de React técnicamente, pero en realidad no crean DOM por sí mismos. Si bien puede parecer que el <Router> en sí está siendo renderizado al 'root', en realidad solo estamos definiendo reglas sobre cómo funciona nuestra aplicación. En el futuro, verá este concepto a menudo: los componentes a veces existen no para crear DOM por sí mismos, sino para coordinar otros componentes que sí lo hacen.

En el ejemplo, <Route> define una regla en la que visitar la página de inicio / renderizará el componente Home en 'root'.

Rutas múltiples

En el ejemplo anterior, la ruta única es muy simple. No nos da mucho valor, ya que ya teníamos la capacidad de renderizar el componente Home sin que el enrutador estuviera involucrado.

La potencia del router React entra cuando usamos varias rutas para definir qué componente debe renderizarse en función de qué ruta está activa actualmente:

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

Cada <Route> renderizará su componente respectivo cuando su ruta coincida con la URL. Solo uno de estos tres componentes se renderizará en 'root' en un momento dado. Con esta estrategia, montamos el enrutador en el DOM 'root' una vez, luego el enrutador intercambia componentes con cambios de ruta.

También vale la pena señalar que el enrutador cambiará de ruta sin hacer solicitudes al servidor, así que imagine que cada componente podría ser una página completamente nueva.

Diseño reutilizable

Estamos empezando a ver los humildes comienzos de una Aplicación de Una Sola página. Sin embargo, todavía no resuelve problemas del mundo real. Claro, podríamos construir los tres componentes para que sean páginas HTML completas, pero ¿qué pasa con la reutilización de código? Lo más probable es que estos tres componentes compartan activos comunes, como un encabezado y una barra lateral, así que, ¿cómo evitamos la repetición de HTML en cada componente?

Imaginemos que estábamos construyendo una aplicación web que se parecía a esta maqueta:

Una maqueta de sitio web simple.

Cuando comience a pensar en cómo esta maqueta se puede dividir en secciones reutilizables, puede terminar con esta idea:

Cómo puedes dividir la maqueta web simple en secciones.

Pensar en términos de componentes y diseños anidables nos permitirá crear piezas reutilizables.

De repente, el departamento de arte le informa de que la aplicación necesita una página para buscar widgets que se asemeje a la página para buscar usuarios. Con la Lista de usuarios y la Lista de widgets que requieren la misma «apariencia» para su página de búsqueda, la idea de tener el Diseño de búsqueda como un componente separado tiene aún más sentido ahora:

Busque widgets ahora, en lugar de usuarios, pero las secciones principales siguen siendo las mismas.

El diseño de búsqueda puede ser una plantilla principal para todo tipo de páginas de búsqueda ahora. Y mientras que algunas páginas pueden necesitar Diseño de búsqueda, otras pueden usar el Diseño Principal directamente sin él:

Un diseño desacoplado.

Esta es una estrategia común y si ha utilizado cualquier sistema de plantillas, probablemente haya hecho algo muy similar. Ahora trabajemos en el HTML. Para empezar, haremos HTML estático sin tener en cuenta 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>

Recuerde, el elemento 'root' siempre estará presente, ya que es el único elemento que tiene el cuerpo HTML inicial antes de que se inicie JavaScript. La palabra «root» es apropiada porque toda nuestra aplicación React se montará en ella. Pero no hay un «nombre correcto» o convención para lo que lo llamas. He elegido «root», así que seguiremos usándolo a lo largo de los ejemplos. Tenga en cuenta que no se recomienda montar directamente en el elemento <body>.

Después de crear el HTML estático, conviértalo en componentes de 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> ); }});

No te distraigas demasiado entre lo que llamo «Diseño» y «Componente». Los tres son componentes de React. Solo elijo llamar a dos de ellos «Diseños», ya que ese es el papel que están desempeñando.

Eventualmente usaremos «rutas anidadas» para colocar UserList dentro de SearchLayout, luego dentro de MainLayout. Pero primero, observe que cuando UserList se coloca dentro de su padre SearchLayout, el padre usará this.props.children para determinar su ubicación. Todos los componentes tienen this.props.children como prop, pero es solo cuando los componentes están anidados que el componente padre obtiene este prop rellenado automáticamente por React. Para los componentes que no son componentes primarios, this.props.children será null.

Rutas anidadas

Entonces, ¿cómo hacemos que estos componentes se aniden? El router lo hace por nosotros cuando anidamos rutas:

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

Los componentes se anidarán de acuerdo con la forma en que el enrutador anida sus rutas. Cuando el usuario visita la ruta /users, el enrutador React colocará el componente UserList dentro de SearchLayout y luego ambos dentro de MainLayout. El resultado final de visitar /users serán los tres componentes anidados colocados dentro de 'root'.

Tenga en cuenta que no tenemos una regla para cuándo el usuario visita la ruta de la página de inicio (/) o desea buscar widgets. Se omitieron por simplicidad, pero pongámoslas con el nuevo enrutador:

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

Probablemente ya haya notado que JSX sigue reglas XML en el sentido de que el componente Route puede escribirse como una etiqueta: <Route />o dos: <Route>...</Route>. Esto es cierto para todos los JSX, incluidos los componentes personalizados y los nodos DOM normales. Por ejemplo, <div /> es JSX válido y se convertirá a <div></div> cuando se renderice.

Por brevedad, imagine que WidgetList se asemeja a UserList.

Dado que <Route component={SearchLayout}> ahora tiene dos rutas secundarias, el usuario puede visitar /users o /widgets y el <Route> correspondiente cargará sus componentes respectivos dentro del componente SearchLayout.

Además, observe cómo el componente Home se colocará directamente dentro de MainLayout sin que SearchLayout esté involucrado, debido a la forma en que las <Route>s están anidadas. Probablemente te imagines que es fácil reorganizar cómo se anidan los diseños y los componentes reorganizando las rutas.

IndexRoutes

El router React es muy expresivo y, a menudo, hay más de una forma de hacer lo mismo. Por ejemplo, también podríamos haber escrito el enrutador anterior de esta manera:

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

A pesar de su aspecto diferente, ambos funcionan exactamente de la misma manera.

Atributos de ruta opcionales

A veces, <Route> tendrá un atributo component sin path, como en la ruta SearchLayout de arriba. Otras veces, puede ser necesario tener un <Route> con un path y un no component. Para ver por qué, comencemos con este ejemplo:

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

La parte /product de la path es repetitiva. Podemos eliminar la repetición envolviendo las tres rutas en una nueva <Route>:

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

De nuevo, React Router muestra su expresividad. Cuestionario: ¿notó el problema con ambas soluciones? Por el momento no tenemos reglas para cuando el usuario visita la ruta /product.

Para arreglar esto, podemos agregar un IndexRoute:

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

Use <Link> no <a>

Al crear anclajes para sus rutas, deberá usar <Link to=""> en lugar de <a href="">. Sin embargo, no se preocupe, al usar el componente <Link>, el enrutador React finalmente le dará un anclaje ordinario en el DOM. Sin embargo, usar <Link> es necesario para que React Router haga parte de su magia de enrutamiento.

Agreguemos algunos enlaces (anclajes) a nuestro MainLayout:

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

Los atributos de los componentes <Link> se pasarán al anclaje que creen. Así que este JSX:

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

Se convertirá en esto en DOM:

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

Si necesita crear un anclaje para rutas que no sean de enrutador, como un sitio web externo, use etiquetas de anclaje normales como de costumbre. Para obtener más información, consulte la documentación de IndexRoute y el enlace.

Enlaces activos

Una característica interesante del componente <Link> es su capacidad de saber cuándo está activo:

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

Si el usuario está en la ruta /users, el enrutador buscará anclajes coincidentes que se hicieron con <Link> y cambiará su clase active. Ver más sobre esta función.

Historial del navegador

Para evitar confusiones, he omitido un detalle importante hasta ahora. El <Router> necesita saber qué estrategia de seguimiento de historial usar. Los documentos de React Router recomiendan browserHistory, que se implementa de la siguiente manera:

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

En versiones anteriores de React Router, el atributo history no era necesario y el valor predeterminado era usar hashHistory. Como su nombre indica, usó un signo hash # en la URL para administrar el enrutamiento de front-end estilo SPA, similar a lo que podría esperar de un Backbone.router js.

Con hashHistory, URLs tendrá este aspecto:

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

¿Qué pasa con aquellos feo cadenas de consulta, aunque?

Cuando browserHistory se implementa, las rutas de acceso más orgánica:

  • example.com
  • example.com/users
  • ejemplo.com / widgets

Sin embargo, hay una advertencia en el servidor cuando se usa browserHistory en el front-end. Si el usuario inicia su visita en example.com y luego navega a /users y /widgets, React Router maneja este escenario como se esperaba. Sin embargo, si el usuario inicia su visita escribiendo example.com/widgets directamente en el navegador, o si actualizar en example.com/widgets, el navegador debe hacer al menos una solicitud al servidor para /widgets. Sin embargo, si no hay un enrutador del lado del servidor, esto entregará un 404:

Cuidado con las URL. Necesitará un enrutador del lado del servidor.

Para resolver el problema 404 desde el servidor, React Router recomienda un enrutador comodín en el lado del servidor. Con esta estrategia, no importa a qué ruta del lado del servidor se llame, el servidor siempre debe servir el mismo archivo HTML. Luego, si el usuario comienza directamente en example.com/widgets, a pesar de que se devuelve el mismo archivo HTML, React Router es lo suficientemente inteligente como para cargar el componente correcto.

El usuario no notará nada extraño, pero es posible que tenga preocupaciones sobre servir siempre el mismo archivo HTML. En los ejemplos de código, esta serie continuará utilizando la estrategia de «enrutador comodín», pero depende de usted manejar el enrutamiento del lado del servidor de la manera que considere adecuada.

¿Se puede usar el router React tanto en el lado del servidor como en el lado del cliente de forma isomórfica? Claro que puede, pero eso está mucho más allá del alcance de este tutorial.

Redireccionar con browserHistory

El objeto browserHistory es un singleton para que pueda incluirlo en cualquiera de sus archivos. Si necesita redirigir manualmente al usuario en cualquiera de sus códigos, puede usar su método push para hacerlo:

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

Coincidencia de ruta

El enrutador React maneja la coincidencia de ruta de manera similar a otros enrutadores:

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

Esta ruta coincidirá cuando el usuario visite cualquier ruta que comience con users/ y tenga algún valor después. Coincidirá con /users/1, /users/143 o incluso /users/abc (que deberá validar por su cuenta).

El router React pasará el valor de :userId como apoyo a UserProfile. Se accede a esta propiedad como this.props.params.userId dentro de UserProfile.

Demostración de enrutador

En este punto, tenemos suficiente código para mostrar una demostración.

Vea la Demo de Pen React-Router de Brad Westfall (@bradwestfall) en CodePen.

Si ha hecho clic en algunas rutas en el ejemplo, puede notar que los botones atrás y adelante del navegador funcionan con el enrutador. Esta es una de las principales razones por las que existen estas estrategias history. Además, tenga en cuenta que con cada ruta que visita, no se realizan solicitudes al servidor, excepto la primera en obtener el HTML inicial. ¿Qué tan genial es eso?

ES6

En nuestro ejemplo CodePen, React, ReactDOM y ReactRouter son variables globales de una CDN. Dentro del objeto ReactRouter hay todo tipo de cosas que necesitamos, como los componentes Router y Route. Así que podríamos usar ReactRouter así:

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

Aquí, tenemos que anteponer todos los componentes de nuestro enrutador con su objeto padre ReactRouter. O podríamos usar la nueva sintaxis de desestructuración de ES6 como esta:

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

Esto «extrae» partes de ReactRouter en variables normales para que podamos acceder a ellas directamente.

A partir de ahora, los ejemplos de esta serie utilizarán una variedad de sintaxis ES6, incluida la desestructuración, el operador de propagación, importaciones/exportaciones y tal vez otros. Habrá una breve explicación de cada nueva sintaxis a medida que aparecen y el repositorio de GitHub que viene con esta serie también tiene muchas explicaciones de ES6.

Junto con webpack y Babel

Como se indicó anteriormente, esta serie viene con un repositorio de GitHub para que pueda experimentar con el código. Dado que se asemejará a los ingredientes de un SPA del mundo real, utilizará herramientas como webpack y Babel.

  • webpack agrupa varios archivos JavaScript en un solo archivo para el navegador.
  • Babel convertirá el código ES6 (ES2015) a ES5, ya que la mayoría de los navegadores aún no entienden todo el código ES6. A medida que este artículo envejezca, los navegadores serán compatibles con ES6 y es posible que Babel no sea necesario.

Si aún no te sientes muy cómodo usando estas herramientas, no te preocupes, el código de ejemplo ya tiene todo configurado para que puedas centrarte en React. Pero asegúrese de revisar el código de ejemplo README.md archivo para documentación adicional del flujo de trabajo.

Tenga cuidado con la sintaxis obsoleta

Buscar en Google información sobre el Router React podría llevarlo a uno de los muchos artículos o páginas de StackOverflow que se escribieron cuando el Router React estaba en su versión anterior a la 1.0. Muchas características del pre-1.las versiones 0 están en desuso ahora. Aquí hay una lista corta:

  • <Route name="" /> está en desuso. Utilice <Route path="" /> en su lugar.
  • <Route handler="" /> está obsoleto. Utilice <Route component="" /> en su lugar.
  • <NotFoundRoute /> está obsoleto. Ver Alternativa
  • <RouteHandler /> está obsoleta.
  • willTransitionTo está obsoleto. Ver onEnter
  • willTransitionFrom está obsoleto. Ver onLeave
  • Las» Ubicaciones «ahora se llaman»historiales».

Vea la lista completa de 1.0.0 y 2.0.0

Resumen

Todavía hay más funciones en React Router que no se mostraron, así que asegúrese de consultar los documentos de la API. Los creadores de React Router también han creado un tutorial paso a paso para React Router y también han revisado este React.Vídeo de configuración de js sobre cómo se creó el router React.

Agradecimiento especial a Lynn Fisher por la obra de arte @ lynnandtonic

Serie de artículos:

  1. Router React (¡Estás aquí!)
  2. Componentes De Contenedor
  3. Redux

Deja una respuesta

Tu dirección de correo electrónico no será publicada.