¡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.
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 <div> 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.Fragmenty 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.
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:
classNameen lugar declass. Puesto queclasses palabra reservada en JavaScript, no la podemos usar para indicar un atributo de clase de un elemento HTML. En su lugar utilizaremosclassName, por ejemplo:<div className="bg-white"></div>.- Los atributos relacionados con eventos utilizan camel-case. Por ejemplo, no escribimos
<input onclick={reaccion} />sino<input onClick={reaccion} />. defaultCheckeden lugar dechecked. El atributocheckedexiste 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 atributodefaultCheckedsí que indica el estado inicial.htmlForen lugar defor, ya que for es una palabra reservada en JavaScript y nos encontramos con el mismo obstáculo que conclass.
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.
El componente raíz de la aplicación
Cada aplicación basada en React tendrá un componente raíz del que derivarán los demás, dado que la estructura de la aplicación tiene forma de árbol, al igual que la estructura de una página HTML.
Al crear un nuevo proyecto con npx create-vite, dispondremos de un componente raíz denominado App dentro del cual implementaremos la interfaz principal.
Modo estricto del intérprete
Además, si te fijas en el fichero src/main.jsx notarás que hay un componente más arriba en la estructura, que se llama React.StrictMode. Este componente activa una serie de comprobaciones de seguridad y de potenciales problemas, que nos avisarán si utilizamos algunas características obsoletas, no seguras o programamos algún componente de forma incorrecta. Actualmente, con StrictMode podremos saber si:
- Algún componente presenta ciclos de vida inseguros para el renderizado concurrente,
- Usamos las referencias basadas en cadenas de caracteres, que están obsoletas
- Alguna de las funciones que deben ser puras tienen efectos secundarios
- Se está usando una API antigua para definir contextos
- Utilizamos
findDOMNode, que está obsoleto
Es conveniente dejar ese componente activo durante el desarrollo de la aplicación, pero podremos eliminarlo al configurarla para producción, ya que añade una sobrecarga innecesaria una vez que la aplicación está terminada.
Modificando el nodo raíz
Volviendo al nodo App, este se puede invocar porque está importado en las primeras líneas del archivo, concretamente, en la que indica:
import App from './App'
Esto quiere decir que su implementación está en el archivo App.jsx del mismo directorio. Si ahora nos dirigimos a ese archivo, veremos el contenido del componente implementado en forma de función:
function App() { const [count, setCount] = useState(0) return ( <div className="App"> <div> <a href="https://vitejs.dev" target="_blank"> <img src="/vite.svg" className="logo" alt="Vite logo" /> </a> <a href="https://reactjs.org" target="_blank"> <img src="{reactLogo} className="logo react" alt="React logo" /"> </a> </div> <h1>Vite + React</h1> <div className="card"> <button onClick={() => setCount((count) => count + 1)}> count is {count} </button> <p> Edit <code>src/App.jsx
and save to test HMR
</p>
</div>
<p className="read-the-docs">
Click on the Vite and React logos to learn more
</p>
</div>
)
} </code>
Para comenzar a crear nuestra aplicación, eliminaremos todo el código no esencial de esta plantilla, quedándonos con lo siguiente:
function App() { return ( <div className="App"> </div> ) }
Antes de empezar a realizar modificaciones, siempre conviene tener lanzado el servicio web con el comando npm run dev y la página web abierta en un navegador, de forma que veamos cualquier cambio o error de forma instantánea.
También podemos eliminar los imports que ya no son necesarios, como el de useState y el de reactLogo. Ahora tendremos un lienzo en blanco para comenzar a construir nuestra aplicación.
Asociando el componente raíz a un elemento del DOM
En el archivo src/main.jsx se llama al método ReactDOM.createRoot() que asocia un elemento del DOM con el nodo raíz de la aplicación React. En este caso, el elemento de la web asociado será el que tenga id="root".
En el caso de que desarrollemos una aplicación sin usar el proyecto por defecto de Vite, tendremos que asociar de forma análoga un elemento de documento HTML original al componente raíz.
Prototipo de la estructura
Aunque el proyecto que vamos a desarrollar a lo largo del curso es una aplicación de gestión de tableros tipo Kanban, por ahora vamos a limitarnos a lo esencial y vamos a crear una lista de tareas. El primer paso es escribir un prototipo de cómo podría quedar la lista de tareas con varias tareas ya incluidas. Por ejemplo, podríamos comenzar con una lista HTML con cada tarea pendiente en un ítem de la lista:
function App() { return ( <div className="App"> <h1>Kanpus</h1> <p>Tareas pendientes:</p> <ul> <li>Aprender componentes React</li> <li>Completar las prácticas del módulo 1</li> <li>Realizar la autoevaluación</li> </ul> </div> ) }
En principio, con estos elementos no nos quedará aún una interfaz muy estética ni muy interactiva, pero nos servirá para identificar un elemento que se repite y que, de añadir complejidad a la aplicación, se debería separar para facilitar la reutilización del código. Por ejemplo, si añadiéramos un checkbox para indicar tareas completadas y un botón para eliminarlas:
<li> <input type="checkbox" defaultChecked={false} /> Aprender componentes React <button>Eliminar</button> </li>
En las próximas lecciones desarrollaremos varios componentes que irán dando forma y añadiendo funcionalidad a nuestra lista de tareas, que próximamente se convertirá en una aplicación de tableros Kanban.
Puedes comprobar que el estilo que se usa en la aplicación web proviene de un archivo CSS llamado App.css que se importa al principio de App.jsx. Modifica este archivo de estilo para adaptar a tu gusto la interfaz de la lista de tareas.
Creando y componiendo componentes
A lo largo de esta lección aprenderás a construir nuevos componentes y a incluirlos para que formen parte de la estructura de otros.
Componente Tarea
Dado que cada elemento de la lista de tareas va a replicar la misma estructura básica, con una casilla para marcar como completada y un botón para eliminar, vamos a crear una nueva función que nos va a definir una tarea genérica. A este nuevo componente lo podemos llamar, simplemente, Tarea. En principio, podemos utilizar el mismo código que teníamos para cualquiera de las tareas:
const Tarea = () => ( <li> <input type="checkbox" defaultChecked={false} /> Aprender componentes React <button>Eliminar</button> </li> )
Fíjate en que estamos utilizando la sintaxis flecha para declarar la función y que, como no tenemos que realizar ningún cómputo sino simplemente devolver el contenido del componente, podemos omitir el uso de llaves y la palabra reservada return. Además, el atributo defaultChecked establece el estado inicial de la casilla como desmarcada.
Ahora que tenemos el componente Tarea listo para reutilizar, nuestro componente App podría quedar como sigue:
<div className="App"> <h1>Kanpus</h1> <p>Tareas pendientes:</p> <ul> <Tarea /> <Tarea /> <Tarea /> </ul> </div>
Simplemente hemos repetido el nodo referente al componente Tarea tres veces para disponer de tres elementos en la lista. Sin embargo, esto nos genera tareas idénticas con el mismo título. A continuación arreglaremos esto utilizando propiedades.
Uso de propiedades en el componente
Para que cada tarea pueda tener un contenido diferente, hemos de proporcionarle una propiedad que contenga el título correspondiente. En el componente podemos recibir el parámetro props (recuerda que la convención es usar este nombre para las propiedades recibidas) y acceder al título:
const Tarea = (props) => ( <li> <input type="checkbox" defaultChecked={false} /> {props.titulo} <button>Eliminar</button> </li> )
Ahora en el componente principal basta con indicar un atributo titulo diferente para cada tarea:
<Tarea titulo="Aprender componentes de React" /> <Tarea titulo="Completar las prácticas del módulo 1" /> <Tarea titulo="Realizar la autoevaluación" />
Reutilización de componentes
Ahora que tenemos un componente Tarea que encapsula las propiedades e interfaz de un elemento de nuestra lista de tareas, vamos a extraer los datos de la parte de interfaz del componente App para simplificar su implementación y hacerla totalmente independiente de la cantidad y contenido de tareas que existan. Separando datos e interfaz
Puesto que la aplicación que estamos desarrollando está aún en fase de prototipado, no nos hemos preocupado de tener un almacén para la información a mostrar. Lógicamente, no tendría cabida que datos como los títulos de las tareas hubiese que escribirlos a mano en la interfaz web. De la misma forma, no tiene sentido que el número de tareas disponibles esté determinado de antemano.
Para separar la información de la parte del componente que simplemente ha de mostrarla, vamos a construirnos un array con la lista de títulos de ejemplo que tenemos:
function App() {
const tareas = [ "Aprender componentes de React", "Completar las prácticas del módulo 1", "Realizar la autoevaluación" ]
// resto del componente...
Una vez que tenemos los títulos almacenados en una estructura de datos, podemos simplificar el marcado recorriéndola con el método map, que devolverá un array de nodos para incluir en el componente:
<ul>
{tareas.map(tarea => <Tarea titulo={tarea} />)}
</ul>
Observa que hemos de enmarcar la sentencia entre llaves para que se interprete como código JavaScript del que queremos recoger un resultado.
En este caso, para cada título en el array tareas se construirá un nodo del componente Tarea con el título correspondiente aplicado. Aunque el método map técnicamente devuelve un array y no un nodo JSX, es también un resultado válido: simplemente se colocan los elementos en el orden en que estuvieran en el array original. Identificador único key
Si consultamos la consola de las herramientas de desarrollo de nuestro navegador, es muy probable que aparezca un mensaje de React avisando de que es necesario indicar un identificador en un atributo key a cada elemento que generemos mediante un bucle como el anterior.
React debe tener un identificador único para cada elemento para llevar un registro de los cambios que sufra y poder actualizar el nodo correspondiente en el DOM. Al crear elementos iterativamente, es importante identificarlos bien por si se crean en un orden distinto en otra ocasión en que se renderice el componente.
Por lo general, el identificador provendrá de los datos almacenados en el servidor y simplemente lo asociaremos como valor a la propiedad key. Por ahora, nos basta con utilizar el índice del bucle para que cada tarea esté identificada de forma única y evitemos el aviso de React:
<ul>
{tareas.map((tarea, i) => <Tarea titulo={tarea} key={i} />)}
</ul>
