Rutas en ReactJS con React Router

Actualización Noviembre 2015: el ejemplo está realizado con React Router 0.13.x. La versión 1.0 introduce bastantes cambios, aunque la filosofía es la misma.

Cuando hace unas semanas veíamos qué es ReactJS explicaba que ReactJS no es un framework «todo incluido» (como Angular o Durandal), sino que es más bien una librería que se limita a la generación de vistas. Esto tiene la ventaja de que nuestra aplicación queda mucho menos acoplada a ReactJS, pero también hace que cuando necesitemos más cosas nos toque buscar opciones y decidir cómo queremos hacerlas, cómo vimos al hablar de tests de extremo a extremo con Nightwatch.

En muchas aplicaciones web actuales se acaban manejando rutas desde la parte cliente, y esa es una de las cosas que ReactJS no incluye de serie, pero que podemos implementar utilizando distintas librerías. En este caso vamos a ver una que me ha gustado especialmente: React Router.

React Router es una libería para gestionar rutas en aplicaciones que utilicen ReactJS. Está inspirada en el sistema de enrutado de Ember.js y su forma de manejar las rutas es un poco diferente de lo que podemos ver en otros sistemas, como ASP.NET MVC, AngularJS, Express o Compojure, por poner unos cuantos ejemplos.

La idea fundamental es que podemos tener rutas anidadas mapeadas a vistas anidadas. ¿Qué quiere decir esto? Vamos a verlo con un ejemplo.

Un ejemplo

Imaginemos el típico dashboard de estadísticas de una web. Tenemos distintos tipos de métricas: Visitantes, Páginas, Orígenes, etc., y para cada una de ellas podemos una visión global con un par de datos significativos y los detalles, que podemos mostrarlos de forma tabular o mediante una gráfica:

dashboard-sample

Existen varias partes de la página que se van a ir repitiendo entre las distintas «subpáginas» del sitio. Todas tendrán las pestañas de la parte izquierda para navegar entre secciones y, dentro de cada sección, tanto en la vista de tabla como la vista de gráfico se repetirá la parte superior en la que aparece el resumen.

Una forma habitual de resolver esto es mediante el uso de plantillas que permitan «incluir» fragmentos de html comunes en varias páginas, de una forma similar a cómo se hace con las Master Pages de ASP.NET MVC o los layouts de Jade.

En el caso de React Router la forma de resolver este tipo de situaciones es ligeramente diferente y se basa en el uso de rutas anidadas. Al definir las rutas, podemos ir definiendo parte de la estructura de la página y delegar en las rutas hijas la generación del resto de la estructura.

Si pensamos en el ejemplo anterior, podríamos tener las siguientes rutas:

route-parts

  • / : Ruta base en la que se define el menú de la izquierda con las distintas secciones del dashboard.
    • visitors : Ruta para visitantes en la que se define el resumen de visitantes
      • table : Ruta con la tabla
      • chart : Ruta con el gráfico
    • … resto de rutas

De esta forma evitamos que repetir nada entre una «página» y otra, y podemos implementarlo todo mediante componentes de ReactJS independientes.

Uso de React Router

La definición de rutas se realiza a través del componente Router:

var routes = (
  <Route handler={App} path="/">
       <Route name="visitors" handler={Visitors}>
            <Route name="visitors-table" path="table" handler={VisitorsTable}/>
            <Route name="visitors-chart" path="chart" handler={VisitorsChart}/>
       </Route>
       <Route name="pages" handler={Pages}>
            <Route name="pages-table" path="table" handler={PagesTable}/>
            <Route name="pages-chart" path="chart" handler={PagesChart}/>
       </Route>
       {/* ... resto de rutas */}
  </Route>
);

Router.run(routes, function(Handler) {
  React.render(<Handler />, document.body);
});

En la tabla de rutas vamos definiendo la jerarquía de rutas, indicando la manera en que están organizadas y el componente que se encargará de renderizar cada una. Ahora sólo falta definir cada componente:

var App = React.createClass({
  render: function() {
    return (
      <div>
        {/* Pintamos el menú de la izquierda */}
        <Menu/>
        {/* Delegamos al router pintar el resto */}
        <RouteHandler/>
      </div>
    );
  }
});

var Visitors = React.createClass({
  render: function() {
    return (
      <div>
        {/* Pintamos resumen */}
        <VisitorDetails/>
        {/* Delegamos al router pintar el resto */}
        <RouteHandler/>
      </div>
    );
  }
});

var VisitorsTable = React.createClass({
  render: function() {
    return (
      <div>
        {/* Pintamos la tabla */}
      </div>
    );
  }
});

La idea es sencilla, cada componente renderiza la parte que le toca y delega a RouteHandler la generación de la parte que es dependiente de la ruta. Esto permite unir de una forma muy intuitiva la estructura de los componentes con la estructura de las rutas de la página.

Por supuesto se pueden hacer muchas más cosas, como definir rutas por defecto, redirigir de una ruta a otra, crear enlaces automáticamente, pasar parámetros entre rutas y todo lo que cabría esperar de un sistema de enrutado moderno. Todo eso lo podéis ver en la documentación de React Router, que además es bastante clara y completa.

Resumen

Una de las cosas que más me está gustando de ReactJS es que no intenta solucionar todos los problemas de golpe y se centra sólo en la parte de generación de vistas. Eso hace que podamos elegir entre distintas implementaciones para el resto de componentes de la aplicación, y en este caso concreto de las rutas, la solución propuesta por React Router y sus rutas anidades es muy interesante.

Hay un parte que no tengo muy claro todavía si me gusta o no, y es el uso de componentes de ReactJS para la definición de la tabla de rutas. Por una parte, la definición de la tabla de rutas como tal creo que se podría desacoplar más de JSX y hacerla con un API diseñada para ser usada con Javascript «puro», pero por otra parte la naturaleza jerárquica de las rutas encaja muy bien con el pseudo XML de JSX a la hora de anidarlas.

Supongo que para los que estén acostumbrados a Ember.js (que es de dónde está copiado este sistemma) no será nada nuevo, pero a mi esta idea de las rutas anidadas me ha parecido más cómoda de manejar que los sitemas de había usado anteriomente con AngularJS o ASP.NET MVC. Aun así, si prefieres una alternativa más parecida a ellos, puedes usar React Router Component.

4 comentarios en “Rutas en ReactJS con React Router

  1. angel calderaro dijo:

    excelente articulo! no me gustaba react-router pero ahora veo que si tiene lo suyo.

  2. Pingback: Desarrollando se vive mejor (VI): React, React y más React | el.abismo = de[null]

  3. Hola. Muy buen artículo.

    Recientemente estaba trabajando en un proyecto con react-router-dom 4.1.1 y webpack, para colocar mi aplicación en producción. Todo iba muy bien hasta que usé webpack:

    Ahora hago clic en «Vendedores» y esto me redirige a localhost:3000/vendedores. Pero después refresco la dirección y me aparece un horrible «Cannot GET /vendedores”.

    Ahora entiendo que esto se debe a que estoy solicitando un archivo al servidor y éste no lo encuentra. Pero antes de usar webpack, ésto sí funcionaba.

    ¿Qué me recomiendas hacer para que las rutas funcionen en producción? Gracias de antemano.

  4. El problema es que el router está usando el sistema de rutas de html5, y para eso necesitas configurar tu servidor (y webpack en este caso) para que cualquier petición a /* te devuelva el contenido de index.html (ya que desde ahí el propio react-router tomará el control).

    Una alternativa es usar el fallback tradicional y montar las rutas sobre el hash de la página, con lo que las urls quedarían localhost:3000/#vendedores.

Comentarios cerrados.