Esta entrada está orientada a aquellos desarrolladores que quieran empezar a trabajar en aplicaciones web con React y Redux. Mediante un ejemplo sencillo de un catálogo de libros, se irán explicando los conceptos básicos de estas dos librerías de JavaScript para que los puedas poner en práctica lo antes posible. Tienes disponible el proyecto completamente funcional aquí.
React
¿Qué es React?
React es una librería de JavaScript utilizada para desarrollar interfaces de páginas web. Está basada en componentes que manejan su propio estado y se esctructuran entre ellos formando interfaces gráficas más complejas.
¿Qué es un componente?
Un componente representa una pequeña parte de la aplicación de forma que la vista de la aplicación queda dividida en unos cuantos componentes que realizan una tarea concreta. Por ejemplo, en nuestra aplicación de ejemplo tendremos una lista de libros (el componente BookList) y un componente que se encargará de mostrar los detalles del libro (BookDetail). Parece evidente que ambos componentes pueden mostrarse en una sola pantalla simultáneamente, pero para explicar los Routers de React, concepto que se explicará más adelante, haremos que cada componente se muestre en una pantalla distinta.
La forma de crear un componente es muy sencilla. Hay dos manera de hacerlo: creando un componente de tipo clase o de tipo función:
-
- De tipo clase
El componente es una clase que hereda de la clase Component dentro de React. El componente sobrescribe el método render, que devuelve código JSX (JavaScript XML, una extensión de JavaScript muy similar a HTML). Este código puede contener otros componentes que ya han sido creados. Esta clase debe ser exportada para que el componente pueda ser utilizado por otros.
-
- De tipo función
Son componentes que no tienen un estado, ya que almacenan toda la información necesaria en la variable props. La función que representa el componente devuelve el código JSX que dará forma al componente.
En un principio, si un componente no accede al estado de la aplicación será más sencillo de implementar que uno de tipo clase. La función que representa al componente es equivalente a la función render de los componentes de tipo clase. Los componentes de tipo función se centran en la interfaz de usuario de la app, no en el comportamiento.
¿Qué es el estado de una app?
El estado de una aplicación es una variable que contiene el conjunto de datos que serán accesibles por todos los componentes independientemente de si tienen una relación de padre e hijo entre ellos. A diferencia de la variable props, el estado es tanto de lectura como de escritura. Nuestra aplicación tendrá un estado formado por la lista de libros y un libro seleccionado. Como ambos componentes (BookList y BookDetail) van a acceder al estado de la aplicación, serán componentes de tipo clase.
Redux
¿Qué es Redux?
Redux es una librería de JavaScript que nos ayuda controlar el estado de la aplicación y estructurar las relaciones entre componentes y estado de una manera sencilla. Para ello se utilizan Action Creators, Actions y Reducers. Es la parte encargada del Controlador dentro del patrón MVC.
¿Qué son los Action Creators, Actions y Reducers?
Estos 3 elementos trabajan de la mano para alterar el estado de una aplicación. ¿Cómo? El Action Creator crea una acción que será procesada por el Reducer alterando el estado de la aplicación. La forma de crearlos es muy sencilla:
-
Un Action Creator no es más que una función que crea una acción. El Action Creator se encargará de realizar la operaciones que sean necesarias (acceder a la base de datos o realizar una petición web, entre otras) y devolverá una acción.
- Una acción es un objeto en JavaScript plano que tiene un tipo (type) y la información con la que se va a alterar el estado (payload).
Por ejemplo, una acción que se encarga de obtener la lista de libros sería así:
const action={type: GET_BOOKS, payload: books};
//GET_BOOKS es una constante y books es un array de libros
Mientras que una acción que se encarga de cambiar el libro seleccionado sería así:
const action={type: GET_BOOK, payload: book};
//GET_BOOK es una constante y book el libro seleccionado
- Una vez se crea una acción, una función reductora (o Reducer) se encargará de procesar esa acción. La función Reducer recibe la acción y el estado anterior a que se crease la acción. Esta función nos devolverá el nuevo estado de la aplicación dependiendo del tipo de acción que se haya creado.
React-Redux
Como se puede observar, React y Redux van completamente por separado. Uno se encarga de la Vista y el otro del Controlador. ¿Cómo hacemos para que ambas librerías trabajen de la mano? Con una tercera librería llamada react-redux.
Esta librería se encarga de conectar los componentes creados con React y sus props con los ActionCreators de modo que al realizarse un cambio en el estado de la aplicación los componentes correspondientes se renderizan de nuevo. Para ello se utiliza el método connect de react-redux.
connect(mapStateToProps,mapDispatchToProps)(Componente);
- mapStateToProps es una función que mapea los valores del estado de la app a los valores de la variable props del componente, de modo que puede accederse a los campos del estado mediante
this.props.variable_del_estado
- mapDispatchToProps es una función que se encarga de mapear Action Creators a funciones implementadas en el componente, de modo que los Action Creators pueden ejecutarse de la siguiente forma
this.props.actionCreator()
- Componente es el componente con el que estamos trabajando (la clase).
Redux Middleware
En Redux el uso de Middleware puede ser de gran ayuda en muchas ocasiones permitiéndonos, entre otras cosas, las llamadas asíncronas a una API. La clave de un middleware en React se basa en que puede modificar, detener o cancelar una acción antes de que el reducer la procese.
Un ejemplo sencillo es la utilización de las promesas (promise). Una promesa es un objeto en JavaScript que se crea al realizarse una acción asíncrona (una consulta mediante la librería Axios, por ejemplo).
La librería Axios nos permite realizar consultas mediante el método get. El método get nos devuelve una promesa, de modo que en el momento de generar una acción cuyo payload es el resultado de la consulta, estamos enviando una promesa a la función reducer (en lugar del resultado de la consulta). ¿La solución? Utilizar el middleware redux-promise.
Este middleware se encargará de analizar una acción antes de que el reducer la reciba. Si el payload de la acción es una promesa, el middleware detendrá la acción hasta que la promesa sea resuelta. Cuando esto ocurra, el payload de la acción pasará de ser una promesa a ser el resultado de la consulta realizada por el action creator y el middleware retomará la acción para que llegue al reducer.
Para obtener la lista de libros de nuestra aplicación realizaremos peticiones get al API Rest Fake Rest API.
React Routers
Es muy común utilizar React para desarrollar Single Page Apps (Aplicaciones que están formadas por una sola página en la que sólo hay un fichero HTML). Para configurar qué componentes se mostrarán en pantalla dependiendo de la url introducida, se utilizan los Routers de React, de modo que el usuario tendrá la sensación de estar navegando por diferentes páginas cuando realmente la web está formada por una sola.
Para ello basta con añadir un Router al componente raíz de la aplicación en el que cada ruta mostrará un componente en pantalla. Es importante escribir las rutas de la más restrictiva a la menos restrictiva para evitar problemas.
<BrowserRouter> <div> <Switch> <Route path="/book/:id" component={BookDetails}></Route> <Route path="/" component={BookList}/> </Switch> </div> </BrowserRouter>
Testing
Para testear nuestra aplicación podemos realizar tests unitarios o tests de integración.
-
El test unitario consiste en realizar pruebas a un componente concreto, como comprobar que un botón funciona o que una lista tiene tantos elementos como los esperados.
-
Un test de integración consiste en realizar tests a toda la aplicación (como comprobar que al hacer click en un elemento de la lista de libros se muestra el componente BookDetail con los datos del libro seleccionado).
Nuestras pruebas de testeo las programaremos en los ficheros nombre_del_fichero_a_testear.test.jsx
dentro de la carpeta __test__
. Estos ficheros contendrán llamadas a la función it
.
it('breve explicación de la prueba', ()=>{
//Desarrollo de la prueba
});
Conclusión
Componentes, estados, acciones, reducers, middleware, routers… Lo mejor es que, después de esta breve introducción, te pongas manos a la obra con un pequeño ejemplo o echando un vistazo al proyecto completo para ver cómo se estructura un proyecto que utiliza todas estas tecnologías) y compruebes si ha quedado claro.