Herramientas de usuario

Herramientas del sitio


informatica:programacion:cursos:desarrollo_web_react_18:logica_de_componentes

¡Esta es una revisión vieja del documento!


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:

  • App
  • ListaTareas
  • Tarea
  • Cabecera

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:

  • tipo puede ser o bien una cadena de caracteres especificando el elemento HTML correspondiente, o bien un componente ya existente
  • propiedades son un objeto que declara el valor de los atributos que queramos asignar
  • descendientes son 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 componente Modal representando 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 propiedad ref se 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.

Restricciones y convenciones sintácticas

A la hora de escribir componentes en React, nos encontraremos con algunas limitaciones que presenta esta biblioteca por diversos motivos y otras convenciones de uso. Es importante que las conozcas y las tengas en cuenta para evitar que obstaculicen el proceso de desarrollo; ya que, en caso contrario, es probable que te tropieces con ellas constantemente.

En esta lección veremos una lista de estas circunstancias que hay que considerar y qué efectos tienen en el código que escribimos. Un componente solo puede devolver un elemento de primer nivel

Una de las restricciones más básicas que se nos plantean al desarrollar componentes es que deben estar formados por un solo elemento de primer nivel, que a su vez pueden contener más elementos como hijos. Es decir, no podríamos escribir un componente como el siguiente:

Error al devolver dos elementos de primer nivel const Formulario = () ⇒ ( <input type=“email” placeholder=“ejemplo@example.org”> <input type=“submit” value=“Iniciar sesión”> ) Para arreglarlo, únicamente sería necesario envolver ambos elementos en uno que los contenga: Error al devolver dos elementos de primer nivel const Formulario = () ⇒ (

  <form>
      <input type="email" placeholder="ejemplo@example.org">
      <input type="submit" value="Iniciar sesión">
  </form>

)

Es posible que no nos interese incorporar este elemento único de primer nivel al componente, “ensuciando” el DOM de nodos tipo

o <span> que no jueguan rol alguno en la interfaz de nuestra web. Existe un atajo para solventar este problema, denominado React.Fragment. Un Fragment es un tipo de componente especial propio de React que no genera ningún marcado en el DOM final. Dicho de otro modo: es un nodo fantasma que no se traduce en ningún elemento en la página web.

Hay dos formas de utilizarlo:

  Importar React.Fragment y utilizarlo como un componente más (<React.Fragment>{ elementos }</React.Fragment>)
  Si tenemos Babel ya configurado (como en nuestro proyecto Vite), es utilizar marcas HTML aparentemente vacías <>...</>, de la siguiente manera:

Error al devolver dos elementos de primer nivel const Formulario = () ⇒ ( <> <input type=“email” placeholder=“ejemplo@example.org”/> <input type=“submit” value=“Iniciar sesión”/> </> ) Babel entenderá la sintaxis y la traducirá a un React.Fragment en el código final. Las etiquetas siempre deben cerrarse A diferencia de HTML5, que soporta algunas etiquetas sin cierre como <img>, <input> o <p>, en sintaxis JSX todas las etiquetas deben tener su correspondiente etiqueta de cierre, como en <Boton>Púlsame</Boton>, o bien estar auto-cerradas, por ejemplo <Formulario />. En cualquier caso, el compilador de JSX proporciona un mensaje de error bastante informativo cuando nos dejamos una etiqueta sin cerrar. Puedes comprobarlo eliminando la etiqueta de cierre o la barra inclinada en algún elemento HTML o componente, y verás un mensaje similar al siguiente: Expected corresponding JSX closing tag for <Tarea>. (61:12) Convenciones de nombres Es habitual que los nombres de los componentes comiencen por mayúscula y utilicen Pascal-case para separar palabras. El resto de las funciones, las variables y las constantes, por lo general, se escriben con la primera letra en minúscula, con notación camel-case. Por tanto, nuestros componentes se llamarán BotonPrimario o FormularioBusqueda, mientras que las variables serán estaActivo, contenido, etc. Existen ciertas categorías de funciones que son de uso muy común en React y, por tanto, siguen convenciones para sus nombres también. Aunque los estudiaremos a fondo más adelante, debes saber que un hook se reconoce porque su nombre comienza por use, por ejemplo: useState, useEffect, useContext. Asimismo, un reducer suele acabar con la palabra Reducer, por ejemplo: rootReducer, listaCompraReducer. Además, la mayoría de atributos HTML que se escriben normalmente en minúscula (o con guion) pasan a escribirse en camel-case. Por poner algunos ejemplos: onClick, autoFocus. OJO: la excepción a esta regla son los atributos de accesibilidad, que comienzan por aria- como aria-label o aria-required que se escriben igual que en HTML. Atributos HTML que cambian de nombre Hay algunos atributos que no se pueden utilizar directamente en JSX y debemos sustituirlos por el nombre apropiado: className en lugar de class. Puesto que class es palabra reservada en JavaScript, no la podemos usar para indicar un atributo de clase de un elemento HTML. En su lugar utilizaremos className, por ejemplo:

. Los atributos relacionados con eventos utilizan camel-case. Por ejemplo, no escribimos <input onclick={reaccion} /> sino <input onClick={reaccion} />. defaultChecked en lugar de checked. El atributo checked existe pero no determina el valor inicial del estado (marcada o no marcada) de una casilla, sino el actual. Por tanto, si lo fijamos será imposible que el usuario desmarque o marque la casilla a voluntad, ya que no podrá cambiar de estado. El atributo defaultChecked sí que indica el estado inicial. htmlFor en lugar de for, ya que for es una palabra reservada en JavaScript y nos encontramos con el mismo obstáculo que con class. Ausencia de efectos secundarios en componentes Los componentes creados como funciones, deben evitar a toda costa presentar efectos secundarios, esto es: modificar variables globales, alterar el estado de otros componentes u otros elementos de la página web, generar números aleatorios, etc. Esto se debe a que React espera que la función de renderizado de un componente se pueda ejecutar repetidamente con los mismos parámetros sin que eso altere el resultado en cuanto a la interfaz que genere. Por tanto, debemos cuidar que nuestro código no genere efectos inesperados. En tal caso, puede que React lo detecte y nos informe con un warning o un error, o bien pase indetectado y cause comportamientos impredecibles. Más adelante veremos cómo aislar los efectos secundarios que sean necesarios en estos componentes, con el hook useEffect.

informatica/programacion/cursos/desarrollo_web_react_18/logica_de_componentes.1709199687.txt.gz · Última modificación: por tempwin