¡Esta es una revisión vieja del documento!
Tabla de Contenidos
Lógica de componentes
Notas del curso Desarrollo de aplicaciones web con React 18
Como ya has podido observar, React dispone de las herramientas suficientes para comenzar de forma muy sencilla a construir aplicaciones interactivas. Para poder aprovechar su potencial, tenemos que plantear una aplicación que requiera cierta complejidad y separarla en unidades que tengan responsabilidad única: los componentes.
Diseño de la aplicación
Con el objetivo de construir una aplicación incrementando progresivamente la complejidad y la funcionalidad, en este módulo desarrollaremos el prototipo de una sencilla aplicación para gestión de tareas, como primer ejemplo con React.
A continuación tienes el aspecto aproximado de cómo quedará la aplicación una vez que hayas completado todas las lecciones de este módulo:
De momento nos centraremos en los mecanismos que ofrece React para mostrar la información de la que dispongamos. Esto incluye el desarrollo de la interfaz en sí, la combinación de varios componentes y elementos en otros componentes, el paso de datos y la capacidad de modificar, mostrar u ocultar partes de la interfaz en función de los valores de esos datos.
Esto quiere decir que aún no vamos a tener las herramientas para que la aplicación responda a eventos como pulsaciones de botones o cambios de estado de las casillas de verificación, pero vamos a sentar las bases para que añadir ese comportamiento sea un proceso sencillo.
En los archivos asociados al módulo encontrarás una versión de esta aplicación con los siguientes componentes:
AppListaTareasTareaCabecera
Este último componente se ha dejado como ejercicio práctico, ya que te será muy fácil de completar cuando hayas realizado el aprendizaje de las lecciones. Recuerda implementarlo para comprobar que dominas las técnicas explicadas.
Arquitectura de componentes de React
El objetivo de React es permitir desarrollar una interfaz de aplicación web compleja a partir de componentes sencillos y reutilizables. Estos componentes encapsularán tanto la estructura HTML como los datos que mostrarán.
En la práctica, cada componente consistirá en una función (o clase) que es capaz de proporcionar un nodo del documento que conforma la página web.
Para probar pequeños componentes sin necesidad de construir un proyecto React cada vez, puedes utilizar un servicio de REPL online como CodeSandbox, PlayCode o Repl.it, que permiten escribir código React y ver el resultado en tiempo real.
La función createElement()
Aunque más adelante nos ahorraremos escribirla repetidamente gracias a un poco de azúcar sintáctica, es importante que sepas que la principal funcionalidad encargada de enlazar los elementos HTML en el DOM virtual de React es createElement(). Esta función permite indicar cuál es el elemento principal a crear cuando se construye un componente, incluidos sus atributos o propiedades y sus nodos descendientes. Por eso la vamos a utilizar inicialmente.
La estructura de una llamada a createElement() es la siguiente:
React.createElement(tipo, propiedades, ...descendientes)
donde:
tipopuede ser o bien una cadena de caracteres especificando el elemento HTML correspondiente, o bien un componente ya existentepropiedadesson un objeto que declara el valor de los atributos que queramos asignardescendientesson los nodos hijos del elemento, creados también con la misma función.
Veamos un pequeño ejemplo donde construimos un componente que consiste en un formulario de búsqueda, haciendo uso de createElement():
React.createElement( "form", // tipo { action: "/search", method: "GET" }, // propiedades React.createElement( // hijo 1 "input", { type: "search", placeholder: "Términos de búsqueda" } ), React.createElement( // hijo 2 "button", { type: "submit" }, "Buscar" ) )
Como puedes observar, el concepto es relativamente similar a la creación de elementos HTML en JavaScript puro, donde contamos con los métodos document.createElement() y appendChild().
Una ventaja de programar algo así con React es que la sintaxis es principalmente declarativa, en lugar de la sintaxis imperativa habitual en JavaScript, lo cual consigue un código más compacto. Además, la función de React también es válida para crear componentes de React, no solo elementos HTML.
Esqueleto básico del componente
A la hora de escribir componentes en React, por lo general comenzaremos siempre con el mismo esqueleto básico: una función que devuelve elementos creados con la función React.createElement(). A continuación puedes observar el mismo formulario de búsqueda que hemos construido anteriormente, convertido en un componente al devolver el elemento de tipo form en una función:
// Nombre del componente; const FormularioBusqueda = () => { // Aquí, opcionalmente, van cálculos previos a la construcción de los elementos // se generan los elementos HTML contenidos en el componente: return React.createElement( "form", { action: "/search", method: "GET" }, React.createElement( "input", { type: "search", placeholder: "Términos de búsqueda" } ), React.createElement( "button", { type: "submit" }, "Buscar" ) ) }
Una vez que disponemos de esta función FormularioBusqueda, podemos utilizarla de forma idéntica a como si se tratase de otro elemento HTML, en otro componente con React.createElement(FormularioBusqueda). Por ejemplo, podríamos tener un componente Cabecera que tiene un elemento de tipo FormularioBusqueda:
const Cabecera = () => { return React.createElement( "div", null, React.createElement("h1", null, "Mi primera app React"), React.createElement(FormularioBusqueda) ) }
Como puedes observar, la idea es que tratemos a los componentes como si fueran elementos HTML más complejos.
Propiedades
Los componentes pueden recibir algunos parámetros que sirven para mostrar diferente información en función de la instancia del componente que se esté renderizando. Convencionalmente, estos parámetros o propiedades se recogen en un objeto denominado props.
Por ejemplo, supongamos que estamos desarrollando un componente Desplegable que consistirá en un elemento details con su correspondiente summary pero al que más adelante añadiremos estilos y marcado específico. Sería apropiado tener al menos una propiedad para especificar el título del contenido desplegable, y otra para indicar el propio contenido. En ese caso, el componente tendría aproximadamente el siguiente aspecto:
// admite un objeto de propiedades const Desplegable = (props) => { return React.createElement( "details", null, // utiliza la propiedad titulo React.createElement('summary', null, props.titulo), // utiliza la propiedad contenido React.createElement('p', null, props.contenido) ) }
Las propiedades que recibe un componente son de solo lectura, es decir, no deben modificarse en el cuerpo de la función. Se pueden utilizar para calcular otros valores, pero no deben modificarse ya que puede afectar al ciclo de actualizaciones de componentes.
Hay algunos trucos que nos permiten simplificar el código en componentes pequeños. Si el componente no recibe muchas propiedades y el objeto props no es estrictamente necesario, podemos recoger directamente los valores de las propiedades en constantes mediante desestructuración del objeto. Además, si no hay más sentencias que la de retorno, podemos obviar las llaves y la palabra reservada return:
const Desplegable = ({ titulo, contenido }) => ( React.createElement( "details", null, React.createElement('summary', null, titulo), React.createElement('p', null, contenido) ) )
Para proporcionar valores a las propiedades de un componente, lo haremos de la misma forma que utilizamos cuando asignamos atributos a un elemento HTML, con especial cuidado de que coincidan las claves del objeto con los nombres de propiedades que se consultan en el componente:
React.createElement( Desplegable, { titulo: "Más detalles", contenido: "Contenido detallando el producto" } )
Lógicamente, si en lugar de contenido hubiésemos llamado, por ejemplo, detalles a ese parámetro, el párrafo p que se crea en el componente Desplegable no contendría ningún texto. Aunque ahora lo veas claro porque estamos escribiendo el objeto de propiedades “tal cual”, más adelante nos ayudaremos de una sintaxis más simple y habrá que prestar atención a este detalle.
La captura de pantalla que puedes ver a continuación contiene el prototipo de una aplicación React utilizando los componentes que acabamos de desarrollar:
Propiedades especiales
Hay algunos nombres de propiedades que están reservados para casos especiales. Por un lado, tenemos children que siempre es accesible desde dentro del componente y, por otro, tenemos key y ref que no son accesibles ya que sirven para gestionar el comportamiento de React:
children: esta propiedad permite acceder a los nodos hijo del componente que estamos implementando, si los tuviera. Por ejemplo, podríamos tener un componenteModalrepresentando una ventana de la interfaz que pueda albergar cualquier contenido, que se incluiría como nodos hijo y se accedería en esta propiedad. Más adelante veremos otros usos útiles de esta funcionalidad.key: esta propiedad sirve para diferenciar elementos similares que se hayan creado, por ejemplo, en un bucle.ref: en ocasiones, en la lógica de un componente necesitamos una referencia a un elemento HTML o a otro componente para poder realizar cambios sobre él. Por ejemplo, pausar o reproducir un vídeo, o poner el foco en un campo de entrada. La propiedadrefse coloca en un elemento para disponer de esta referencia.
La sintaxis JSX
Hemos visto que un componente se encarga de renderizar una parte de una página web y lo hace de forma que es reutilizable. Sin embargo, si nuestra intención es componer toda la interfaz en base a estos componentes, puede ser tedioso estar escribiendo tanto código JavaScript, especialmente tantas llamadas a la función React.createElement().
Para solucionar este obstáculo, se introdujo una sintaxis que facilita el desarrollo de componentes al asimilarse lo máximo posible a HTML, a la vez que permite introducir las variables y los valores que necesitemos integrar en la interfaz. Esta sintaxis se denomina JSX, y no es HTML válido ni JavaScript válido, por lo que se requiere una biblioteca de traducción que la convierta en JavaScript estándar.
Escribiendo componentes HTML en JSX
Vamos a ver un ejemplo de cómo podemos escribir un componente sencillo utilizando JSX. Para ello, recuperamos uno de los ejemplos que desarrollamos previamente:
const FormularioBusqueda = () => { return React.createElement( "form", { action: "/search", method: "GET" }, React.createElement( "input", { type: "search", placeholder: "Términos de búsqueda" } ), React.createElement( "button", { type: "submit" }, "Buscar" ) ) }
La traducción de JSX a JavaScript es gracias a Babel, que ya viene instalado y configurado en Vite.
Para convertir a JSX, simplemente lo escribimos como si se tratase de HTML, envuelto entre paréntesis para evitar problemas con los saltos de línea:
const FormularioBusqueda = () => { return ( <form action="search" method="GET"> <input type="search" placeholder="Términos de búsqueda" /> <button type="submit">Buscar</button> </form> ) }
Puedes comprobar cómo Babel traduce JSX a código JavaScript en el intérprete online de Babel, que es el que estamos usando en la imagen inferior. Como verás, el resultado es muy similar al que tenemos escrito más arriba.
Uso de JavaScript en JSX
Al usar JSX no se añade ninguna limitación con respecto a cuando usamos puro JavaScript, y en consecuencia, tenemos también una sintaxis para insertar valores de variables y los resultados de evaluar código JavaScript dentro del propio marcado estilo HTML. Esta consiste en usar llaves para indicar el código que hay que interpretar. Por ejemplo, considera el siguiente componente donde se saluda a la persona que ha iniciado sesión en nuestra página web:
const Saludo = ({ nombreUsuario }) => { const mensaje = `¡Hola ${nombreUsuario}!` return React.createElement("div", null, mensaje) }
El elemento div podemos traducirlo de forma sencilla a JSX, y para que el nodo de texto sea el mensaje, simplemente lo envolveremos en llaves como sigue:
const Saludo = ({ nombreUsuario }) => { const mensaje = `¡Hola ${nombreUsuario}!` return <div>{mensaje}</div> }
Observa que, en este caso, no ha sido necesario indicar los paréntesis que envuelven al elemento devuelto, puesto que el marcado comienza en la misma línea que la palabra reservada return.
Una alternativa que lo simplificaría aún más sería calcular el mensaje directamente en la parte de JSX, eliminando así la necesidad de llaves para el cuerpo de la función:
const Saludo = ({ nombre }) => <div>{`¡Hola ${nombre}!`}</div>
Como ves, con estas técnicas podemos incluso desarrollar pequeños componentes que ocupen una o pocas líneas de código. Esto es especialmente útil cuando queremos componer elementos sencillos reutilizables como botones personalizados o envolturas para otros componentes.
Todas las etiquetas deben ir cerradas. Aunque en HTML5 se pueden usar etiquetas sin cerrar (como <br> o <input>), en el caso de JSX hay que cerrarlas para evitar errores de sintaxis: <br /> o <br></br>.
Uso de componentes en JSX
La sintaxis JSX no solo sirve para especificar elementos HTML en la interfaz de nuestro componente, sino también otros componentes de forma similar a como se hace con React.createElement(). Por ejemplo, integrar un componente Saludo en otro tipo Cabecera sería tan sencillo como tratarlo como un elemento más:
const Cabecera = () => ( <div> <h1>Mi primera app React</h1> <FormularioBusqueda /> <Saludo nombre="Ana" /> </div> }
La propiedad nombre con el valor “Ana” la hemos proporcionado como si se tratase de un atributo HTML. También es posible utilizar la sintaxis de llaves para calcular el valor de una de estas propiedades:
const Cabecera = ({ nombrePila, apellido }) => ( <div> <h1>Mi primera app React</h1> <FormularioBusqueda /> <Saludo nombre={`${nombrePila} ${apellido}`} /> </div> }
Al usar la sintaxis JSX no es necesario importar createElement en nuestro código, es decir, podemos quitar la línea import { createElement } from 'react'. Los imports que sean necesarios, los hará Babel por nosotros cuando compile el código JSX a JavaScript.
