Hoy quiero empezar una nueva serie de capítulos sobre una de las librerías de moda en el desarrollo web: React. El auge de esta librería es cada vez mayor, en 1 año ha pasado de ser una alternativa a Angular.js a sobrepasarlo con una gran ventaja. Pero expliquemos qué es React y por qué tiene tanta aceptación.

A diferencia de otros frameworks dónde se busca proveer casi todo lo posible para el usuario a base de patrones y un diseño de arquitectura mucho más cerrado (como puede ser Angujar.js 1.x y su MVC), React se limita a ser la parte que maneja la interfaz (o la "View" de un MVC) de nuestra aplicación. Lo que la hace tan potente es que es perfectamente adaptable a cualquier otra librería o framework que queramos usar. A su vez, 3 de los aspectos mas importantes son:

  • Orientación a componentes. Cada elemento de la interfaz puede ser un componente totalmente independiente capaz de manejar su estado y acciones. A diferencia de Polymer que se apoya sobre el ShadowDOM, React implementa su propio VirtualDOM para renderizar de forma eficiente sólo los elementos necesarios cuando detecta cambios.
  • Sintáxis JSX. Provee (aunque no obliga a usarla) una manera fácil para trabajar como si lo hiciéramos con simples etiquetas HTML, pero mucho más potente ya que podemos embeber código Javascript dentro.
  • Reusabilidad. Cada componente creado puede ser usado donde y cuando queramos dentro de nuestra aplicación o a su vez dentro de otro componente. El límite lo pone el usuario.

Todo lo anterior tiene una pega, y es que, a diferencia de otras tecnologías como Polymer que se basan en implementaciones nativas del navegador (o es lo que pretenden), React necesita de utilidades externas para convertir nuestra aplicación a código que entienda nuestro navegador. Es por ello que haremos uso de Babel y Webpack para "compilar" nuestro código.

Configuración del workspace

Antes de ponernos a trabajar es necesario configurar nuestro entorno para que podamos ir probando sobre la marcha todo lo que vayamos haciendo.

Para ahorrar tiempo usaré un repositorio con un ambiente minimal que tengo para pruebas y luego añadiremos lo necesario para trabajar con React.

git clone https://github.com/nauzethc/webapp-es2015-boilerplate.git my-react-app  
cd my-react-app  
npm install  
npm start  

Tras esto podemos probar a abrir un navegador en la dirección http://localhost:8080/ y deberíamos ver el mensaje "Hello World!". Todo el código de la aplicación irá contenido dentro del directorio app. Podéis probar a cambiar un fichero dentro de ese directorio y comprobaréis como la web se refresca sola con los cambios. Esto es gracias a webpack-dev-server y su plugin para hacer recargas en caliente (hot module replacement).

A continuación instalamos lo necesario para React. Los dos primeros paquetes instalarán la librería en sí. El tercer paquete provee a Babel lo necesario para transformar código JSX a Javascript.

npm install --save react react-dom  
npm install --save-dev babel-preset-react  

Además hay que añadir el preset para que Babel sepa que tiene que usarlo también con ficheros JSX. Basta con modificar la expresión regular (/.jsx?$/ en lugar de /.js$/) y añadir el preset en el fichero bajo la configuración del loader en el fichero webpack.config.js.

...
    module: {
      loaders: [{
        test: /.jsx$/,
        loader: 'babel',
        include: path.resolve('app'),
        query: {
          presets: ['es2015', 'react']
        }
      }, {
...

Creando un componente

Vamos a crear nuestro primer componente con React. Creamos el directorio app/components y dentro un fichero llamado MyComponent.jsx. Dentro del mismo podemos pegar el siguiente código.

import React from 'react';

export default React.createClass({

  render() {
    return (
      <div>
        <h1>Hello {this.props.name}!</h1>
      </div>
    );
  }

});

El método render será el encargado de que nuestro componente renderice su contenido cuando se inicializa o detecta que han habido cambios.

Posteriormente cargamos nuestro componente desde el fichero principal de la aplicación app/index.js.

import './styles/screen.css';  
import MyComponent from './components/MyComponent';  
import React from 'react';  
import ReactDOM from 'react-dom';

ReactDOM.render(<MyComponent name="World" />, document.getElementById('app'));  

Tras esto deberíamos ver lo mismo que antes en la dirección http://localhost:8080/, sólo que ahora lo hemos hecho con un componente en React. Vamos por tanto a implementar que el componente pueda hacer algo por sí mismo, en este caso haremos que al hacer click sobre el mismo se invierta la palabra "World".

import React from 'react';

export default React.createClass({

  getInitialState() {
    return { name: this.props.name };
  },

  handleClick(e) {
    this.setState({ name: this.state.name.split('').reverse().join('') });
  },

  render() {
    return (
      <div>
        <h1 onClick={this.handleClick}>Hello {this.state.name}!</h1>
      </div>
    );
  }

});

El método getInitialState le dice al componente cual va a ser el estado inicial del mismo, en este caso name copiará el contenido de la propiedad name. La diferencia entre propiedades y estado es que las primeras solo se pueden modificar desde fuera del componente (sólo lectura), mientras que las segundas solo las puede modificar el mismo componente (privadas).

Como vamos a ejecutar una acción al hacer click, vinculamos dicho evento a un método que también hemos definido con el nombre handleClick y que será quien se encargue de modificar el estado.

Solo queda probar todo y comprobar que funciona.

Definiendo las propiedades

Hemos visto como definir un componente y usar las propiedades que le pasamos desde el componente padre. Pero ¿que pasaría si en el ejemplo anterior no definimos la propiedad name? No se mostraría nada ni tampoco pasaría nada al hacer click.

React nos provee una manera de definir de forma tipada las propiedades que necesita un componente para que funcione de manera correcta.

Lo primero que haremos será cambiar la primera línea para importar directamente el objeto PropTypes y no tener que escribir siempre la ruta completa.

Luego añadimos al componente el atributo propTypes (ojo a la diferencia entre mayúsculas y minúsculas entre la librería y el componente) donde vamos a definir que propiedades necesita el mismo.

En este caso definimos name que será de tipo string y es obligatoria. También añadiremos una mas de nombre reverseOnClick que será un booleano opcional y que usaremos desde handleClick para comprobar si debemos realizar o no la acción.

import React from 'react';

export default React.createClass({

  propTypes: {
    name: PropTypes.string.isRequired,
    reverseOnClick: PropTypes.bool
  },

  getInitialState() {
    return { name: this.props.name };
  },

  handleClick(e) {
    if (this.props.reverseOnClick) {
      this.setState({ name: this.state.name.split('').reverse().join('') });
    }
  },

  render() {
    return (
      <div>
        <h1 onClick={this.handleClick}>Hello {this.state.name}!</h1>
      </div>
    );
  }

});

Podemos comprobar como la consola imprimirá un error si no definimos la propiedad name al usar el componente.

// app/index.js
...
ReactDOM.render(<MyComponent />, document.getElementById('app'));  

Además, necesitamos pasar la propiedad reverseOnClick si que queremos que se ejecute la acción que definimos antes.

// app/index.js
...
ReactDOM.render(<MyComponent name="World" reverseOnClick="true" />, document.getElementById('app'));  

Próximamente

Mi idea de aquí en adelante es intentar enseñar a usar no sólo React sino también casos de uso, diseño de arquitectura (especialmente Flow) y otras librerías que la complementan como Redux.

En este tutorial he usado la definición de componentes usando el constructor React.createClass pero podéis usar también la clase React.Component con la sintaxis de "clases" de ES2015.

Recomiendo echar un ojo a la documentación oficial y probar el tutorial que la acompaña.

"Quizás sea tu mejor amigo, y todavía no te has dado cuenta..." - The Thin Red Line (1999)