Índice de contenidos
1. Introducción
En este tutorial vamos a hablar sobre React y sobre hooks. No hace falta que sepas nada de ambos, yo te cuento en el tutorial todo lo que necesites saber para entenderlo todo. Aunque el objetivo del tutorial es explicar el uso de un hook con funciones, vamos a ver también cómo montar el proyecto. Si todo esto y te lo conoces, puedes ir directamente aquí.
React es una librería Javascript para construir interfaces visuales para los usuarios. Básicamente para construir aplicaciones frontend, y mostrar datos a los usuarios mediante la consulta al backend de los mismos.
Material UI es una librería de componentes visuales para React. Si no vas a customizar los estilos de los componentes sobremanera te puede ayudar mucho a construir interfaces visuales vistosas.
2. Entorno
El tutorial está escrito usando el siguiente entorno:
- Sistema Operativo: Windows 10
- Entorno de desarrollo: Webstorm 2021.1
- npm 6.14.12
- node 14.18.1
3. Creando el proyecto
Antes de empezar si alguien quiere ver el código directamente se lo puede bajar del repo.
Vamos a tener que instalar una versión superior a la 14.0.0 de Node para poder seguir este tutorial. Te recomiendo que te metas en la página y sigas las instrucciones de instalación. Puedes comprobar tu versión con el comando «node -v».
También vamos a necesitar una versión de npm superior a la 5.6. Puedes comprobar tu versión ejecutando el comando «npm –version». Si estás por debajo te recomiendo ir al enlace del párrafo anterior e instalarte la última versión de node, lo que también te va a actualizar npm.
Y ahora viene lo bueno. Desde línea de comandos ya podremos ejecutar «npx create-react-app my-app –template typescript». Os parecerá mentira, pero simplemente para conseguir crear un «Hola mundo» con react y typescript me costó bastante. Y esto es porque acostumbrado a trabajar con Typescript me pensaba, que por defecto ya en el mundo del frontend se utilizaba el mismo. Y nada más lejos de la realidad, el por defecto sigue siendo Javascript. De ahí que tengamos que especificar en el comando anterior, un parámetro template indicando que queremos typescript.
Bueno con esto, ya podemos tirar y empezar con React. Podemos arrancar el servidor con «npm start».
4. Un ejemplo de caso de uso con Material UI
Sobre Material UI solo os voy a recomendar que lo uséis para proyectos donde no se pida un alto grado de customización de componentes, ya que esto nos va a provocar tener que sobreescribir las clases css de los componentes de Material UI, a veces puede resultar tedioso.
Si vais a realizar un proyecto con muchos componentes custom y que tenga que ser «pixel-perfect», lo mejor es que os definan los componentes el equipo de diseño, y vosotros los creáis de cero (siempre que se pueda) aplicando los estilos que os digan.
Para incluir Material UI en vuestro proyecto, mejor que no hagáis caso de lo que os dice la página principal. De forma muy cool te dicen que ejecutes un comando npm install y ya está, «San Sacabó», pero no es cierto, y desgraciadamente no es la única librería a la que le pasa. También he probado con ant-design y peca de lo mismo.
Los comandos que en realidad hay que ejecutar y que hacen que esto funcione están un poco más escondidos en la guía de instalación. Ejecutamos «npm install @mui/material @emotion/react @emotion/styled» y además como en el caso del tutorial vamos a utilizar un componente de selección de fechas necesitamos también una librería que complementa a este componente, y que no viene con el componente por algún motivo. Ejecutamos «npm install date-fns –save».
¿Cuál va a ser nuestro ejemplo? Pues en vez de hacer un hola mundo, vamos a utilizar un caso de uso típico donde creo que utilizar un estado que guarda una función es una resolución al problema muy apropiada. El caso de uso es tener un componente, donde al cambiar de valor, antes de que efectivamente se cambie el valor, se despliegue una ventana modal para confirmar dicho cambio de valor.
¿Por qué me parece una solución apropiada? Pues porque en muchos casos podemos tener campos importantes en una página, que requieren de confirmación para ser cambiados, o que al cambiarlos provocan ciertas acciones, como llamadas al backend, con lo cual, hay que estar seguros del cambio.
Nuestro diseño va a consistir en pasarle una función a la ventana modal, para que cuando queramos cambiar el estado de uno de esos componentes, el botón de «Aceptar» de la ventana modal, sea el que efectivamente haga el «SET» de ese valor. Dependiendo del componente donde pretendamos cambiar el valor, la función será una u otra.
Si dividimos la aplicación en componentes nos va a quedar el componente «App» que es el principal de la siguiente manera.
App.tsx
import React from 'react'; import FormWithConfirmation from "./components/FormWithConfirmation"; function App() { return ( <FormWithConfirmation></FormWithConfirmation> ); } export default App;
Como veis, es muy sencillo, solo pinta el otro componente de nuestra aplicación, «FormWithConfirmation».
4.1. Usando useState con funciones
Vamos ahora a pintar otro componente más complejo que es el que ya usa funciones como estados.
Este otro componente ya es un poco más complejo, quedaría de la siguiente forma.
FormWithConfirmation.tsx
import "./FormWithConfirmation.css"; import React, {useState} from "react"; import {DatePicker, LocalizationProvider} from "@mui/lab"; import AdapterDateFns from '@mui/lab/AdapterDateFns'; import {Box, TextField} from "@mui/material"; import ConfirmChangesModal from "./ConfirmChangesModal"; const FormWithConfirmation: React.FC = (props) => { const BoxStyle = { display: 'flex', flexDirection: "column", } as const const BoxItems = { paddingTop: "100px" } as const const [joined, setJoined] = useState(new Date()) const [confirmationModalOpen, setConfirmationModalOpen] = useState(false) const [selectedFunction, setSelectedFunction] = useState<Function>(() => { }) function displayConfirmationDialog(setNewValue: Function) { setConfirmationModalOpen(true) setSelectedFunction(() => () => { setNewValue() setConfirmationModalOpen(false) }) } return ( <Box sx={BoxStyle}> <Box sx={BoxItems}> <LocalizationProvider dateAdapter={AdapterDateFns}> <DatePicker label="Joined on" value={joined} onChange={(newJoined) => { newJoined && displayConfirmationDialog(() => setJoined(newJoined)) }} renderInput={(params) => <TextField {...params} />} /> </LocalizationProvider> <span className={"Span"}>Frontend technologies will be as mature as backend technologies in {joined.toDateString()}</span> <ConfirmChangesModal open={confirmationModalOpen} onCancelChanges={() => setConfirmationModalOpen(false)} onApplyChanges={selectedFunction}/> </Box> </Box> ) } export default FormWithConfirmation
Lo importante es el selectedFunction y su setter correspondiente. Si os fijáis es de tipo Function, es decir que va a ir guardando una función, y esa función en este ejemplo de formulario con confirmación de campos, van a ser precisamente los setters de cada uno de los campos que queramos confirmar su cambio de valor.
Si os fijáis en el «onChange» del DatePicker, estamos llamando a «displayConfirmationDialog» pasándole una función anónima.
Y el punto más importante y realmente el core de este tutorial es la función «displayConfirmationDialog». Se encarga de cambiar el estado del que depende la renderización de la ventana modal en primera instancia y luego asigna a «selectedFunction» por medio de su setter «setSelectedFunction» una función. Y la parte crítica, es que no podemos pasar la función tal cual, ya que React tratará de resolver dicha función y asignar el valor resultante a la variable. Para evitar eso, y que en nuestro selectedFunction se guarde de verdad una función necesitamos usar la notación () => () =>. De esta forma React no trata de resolver la función sino que asigna la función al estado.
Y entonces el puzzle nos queda resuelto. Cada vez que el usuario pinche en «Cancelar» en la ventana modal, pues como veis simplemente se oculta la ventana modal, y solo en el caso de que el usuario pinche en «Aceptar» se ejecutará la función que hayamos seteado, en este caso justamente el setter sobre el cambio de fecha, haciendo efectivo el cambio.
4.2. Dos formas de añadir css con la nueva versión de Material UI
Desde la versión v5 de Material UI la forma antigua de añadir css con @mui/styles ha sido deprecada.
La primera alternativa que tenemos es yo creo la más simple y directa de todas, y es poner el CSS del componente en un fichero CSS, importarlo en el componente y usar los estilos mediante el tag classname de los componentes. Si queréis un ejemplo, el «span» del tutorial hace uso de esta forma de incorporar css.
La segunda alternativa es con el nuevo api que han incorporado en los componentes, la función «sx». No todos los componentes la incorporan, lo cual creo que lo han hecho para utilizar esta función en precisamente los componentes que sirven para colocar los elementos o para decir como van a ir colocados los elementos dentro de ellos como por ejemplo Box. Es decir, que lo que son paddings con respecto a otras cajas que nos rodean y margins este es un buen sitio para ponerlos, y yo creo que la otra alternativa sería para indicar estilos del propio componente en cuanto a color, tamaño, fuente, todo salvo su posicionamiento respecto a otros elementos.
Yo no soy un experto de CSS así que si alguien tiene otra alternativa u otra forma de hacerlo, que lo deje en los comentarios para que los que leamos esto en el futuro tengamos otras opciones entre las que elegir.
5. Conclusiones
En cuanto a React me está gustando bastante como funciona. Creo que es posible separar en capas, y en distintos ficheros la responsabilidad de pintado de un componente, la lógica de negocio y las llamadas al backend. Me ha sorprendido su facilidad de uso una vez que coges como funciona el tema de los hooks. En cuanto a otras librerías de componentes como Material UI o Ant design creo que están a años luz de tener una documentación usable que de confianza como para empezar un proyecto con ellas.