Componentes
Los componentes permiten separar la interfaz de usuario (UI o user interface) en piezas independientes, reutilizables y pensar en cada pieza de forma aislada.
HTML nos permite crear documentos estructurados con su conjunto integrado de etiquetas, pero no nos permite crear componentes reutilizables y, como mencionamos anteriormente, tampoco nos permite encapsular el comportamiento y la interfaz de usuario de una aplicación web.
Por estas razones, los componentes serán la base de una aplicación web desarrollada con React.
Existen dos formas de definir un componente en React: como función o como clase. En ambos casos, los componentes aceptan entradas arbitrarias (llamadas “props”) y devuelven elementos React que describen lo que debe aparecer en la pantalla.
Funciones (componentes funcionales).
Conceptualmente, estos componentes son como las funciones de JavaScript.
Esta función es un componente de React válido que acepta argumentos que contienen datos (comunmente llamados “props”, de properties o propiedades) y devuelve un elemento de React.
Llamamos a dichos componentes “funcionales” porque literalmente son funciones JavaScript.
1function Welcome(props) {2 return <h1>Hello, {props.name}</h1>;3}
Más adelante veremos más detalles sobre las propiedades de los componentes funcionales.
Clases (componentes basados en clases).
Estos componentes se definen basándose en el paradigma orientado a objetos, es decir, se definen como clases de JavaScript. Aceptan entradas arbitrarias (llamadas “props”) y devuelven elementos React que describen lo que debe aparecer en la pantalla.
Para definir un componente basado en clases, se debe extender (heredar) de la clase React.Component
y definir un método render()
que devuelva los elementos React que describen lo que debe aparecer en la pantalla.
En el ejemplo, el componente Welcome
se define como una clase que hereda de React.Component
y define un método render()
que devuelve un elemento React (en este caso, un elemento <h1>
.
1class Welcome extends React.Component {2 render() {3 return <h1>Hello, {this.props.name}</h1>;4 }5}
Los dos componentes anteriores son equivalentes desde el punto de vista de React.
Tanto los componentes funcionales como los de clase tienen algunas características adicionales que veremos más adelante.
Reconocer componentes
Como hemos mencionado, los componentes definen piezas independientes de la interfaz de usuario de una aplicación web. Esto puede llevarnos a preguntarnos, ¿cómo identificar los componentes de una aplicación web?
En general, no existe una única respuesta a esta pregunta. Sin embargo, podemos seguir algunas recomendaciones para identificar los componentes de una aplicación web:
- Elementos repetidos: si un elemento se repite en la interfaz de usuario, es probable que sea un componente.
Por ejemplo, la barra de navegación de una aplicación web se repite en todas las páginas, por lo que es probable que sea un componente.
Navbar
. La barra de navegación de una aplicación web como Twitter.
A su vez, este componente Navbar
cuenta con otros elementos que se repiten constantemente. Elementos que hacen referencia a las secciones de la aplicación web, los cuales podrían pasar a ser componentes a los que podemos denominar como NavbarItem
.
NavbarItem
. Elementos de la barra de navegación que hacen referencia a las secciones de la aplicación web.
Este componente tiene un título y un ícono, y cada uno de ellos permite navegar a otra sección de la aplicación web.
- Secciones de la interfaz de usuario: si una sección de la interfaz de usuario es compleja, es probable que sea un componente que integra otros componentes. Una vez más, si tomamos el ejemplo de la figura 2.3, podemos identificar que los elementos de la barra de navegación (
NavbarItem
) son componentes que se integran en un componente más complejo, que denominaremosNavbar
.
Navbar
. Barra de navegación en la sección de inicio de una aplicación web como Twitter.
- Elementos con lógica: si un elemento tiene lógica asociada, es probable que sea un componente.
PostList
. Lista de publicaciones en una aplicación web como Twitter.
Por ejemplo, en Twitter podemos identificar un componente que se encarga de listar las publicaciones de los usuarios, al cual denominaremos PostList
. Este componente muestra a su vez otros componentes (Post
) que representan cada publicación de un usuario.
En particular, la lista de publicaciones es un componente con lógica asociada, dado que la aplicación debe obtener las publicaciones relacionadas con el usuario que está navegando en ese momento. Estas pueden ser publicaciones de los usuarios que sigue el usuario que está navegando, publicaciones de los usuarios que siguen al usuario que está navegando, tendencias, etc.
- Elementos reutilizables: si un elemento se puede integrar en diferentes entornos, es probable que sea un componente.
Tomando el componente Navbar
como ejemplo, podemos identificar que este componente se puede integrar en diferentes secciones de la aplicación web. Dependiendo del NavbarItem
la aplicación nos redirigirá a una sección diferente de la aplicación web, pero el componente Navbar
permanecerá igualmente.
Navbar
. Barra de navegación en la sección de inicio de la aplicación web de Twitter.
Navbar
. Barra de navegación en la sección de exploración de la aplicación web de Twitter.
Recomendaciones para identificar componentes
Es una buena práctica identificar los componentes de una aplicación web antes de comenzar a desarrollarla. Esto nos permite tener una idea general de la estructura de la aplicación y nos ayuda a definir la arquitectura de la misma.
Podemos emplear diagramas, mockups, wireframes, o incluso dibujar en un papel para identificar los componentes de una aplicación web.
Por ejemplo, supongamos que deseamos desarrollar una aplicación web para listar las tareas que debemos realizar. En este caso, representada en el siguiente diagrama:
En este diagrama, podemos identificar los siguientes componentes:
Task
: los componentesTask
se utilizan para representar cada tarea en la lista de tareas. Cada componenteTask
tiene un título y una descripción.
TaskList
: el componenteTaskList
se utiliza para representar la lista de tareas. Este componente tiene un título y una lista de tareas.
App
: el componenteApp
se utiliza para agrupar todos los componentes de una sección de la aplicación web. En este caso, el componenteApp
agrupa el componenteTaskList
, pero siempre podemos agregar más componentes si es necesario (por ejemplo, un componenteNavbar
).
En esta ocasión, los componentes Task
, TaskList
y App
son componentes funcionales que definiremos en el archivo App.jsx
, modificando el código que se genera por defecto al crear una aplicación con vite
(tal y como mencionamos previamente).
Entonces podemos comenzar a desarrollar la aplicación web, definiendo el componente App
de la siguiente forma:
1function App() {2 return (3 <div>4 <TaskList />5 </div>6 );7}8
9export default App;
En este caso, el componente App
devuelve un elemento <div>
que contiene un elemento <TaskList>
.
Luego, podemos definir el componente TaskList
de la siguiente forma:
1function TaskList() {2 return (3 <div>4 <h1>Lista de tareas</h1>5 <Task />6 </div>7 );8}
En este caso, el componente TaskList
devuelve un elemento <div>
que contiene un elemento <h1>
, el cual representa el título de la lista de tareas, y un elemento <Task>
.
Finalmente, podemos definir el componente Task
de la siguiente forma:
1function Task() {2 return (3 <div>4 <h2>Comprar alimento para gatos</h2>5 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>6 </div>7 );8}
En este caso, el componente Task
devuelve un elemento <div>
que contiene un elemento <h2>
, el cual representa el título de la tarea, y un elemento <p>
, el cual representa la descripción de la tarea.
De esta forma, hemos definido de manera modular los componentes de nuestra aplicación web.
Como podemos notar, la lista de tareas tiene una única tarea, dado que este ejemplo es muy simple y estático. Sin embargo, podemos ir agregando tareas a medida que avancemos en el desarrollo de la aplicación web y, por ejemplo, almacenar las tareas en una base de datos o consumiendo una API.
API de React
Como mencionamos anteriormente, recomendamos emplear JSX para definir componentes en React, devolviendo elementos React en estos componentes. No obstante, existen otra forma de definir componentes en React, empleando la API de React.
Para comenzar, emlpearemos la lista de tareas que definimos anteriormente para explicar las diferentes formas de definir componentes en React. Particularmente, veremos la definición del componente Task
en JSX y empleando la API de React.
En JSX, el componente Task
se define de la siguiente forma:
1function Task() {2 return (3 <div>4 <h2>Comprar alimento para gatos</h2>5 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>6 </div>7 );8}
Por otro lado, podemos definir el mismo componente empleando la API de React, sin la necesidad de usar JSX. Para esto deberemos importar la librería de React en el archivo donde definamos el componente. Por ejemplo, podemos importar la librería de React de la siguiente forma:
1import React from 'react';2
3function Task() {4 return React.createElement("div", null,5 React.createElement("h2", null, "Comprar alimento para gatos"),6 React.createElement(7 "p",8 null,9 "Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón"10 )11}
En esta ocasión, el componente Task
devuelve que se crea empleando la función React.createElement()
. Esta función los siguientes parámetros:
- El tipo de elemento React que se desea crear (en este caso, un elemento
<div>
). - Un objeto con las propiedades del elemento React (en este caso, no se especifican propiedades).
- Los elementos hijos del elemento React (en este caso, un elemento
<h2>
y un elemento<p>
). Si el elemento React no tiene elementos hijos, se puede especificarnull
,undefined
ofalse
. Si el elemento React tiene contenido de texto, se puede especificar como un string.
Como podemos observar, la API de React es más compleja y menos intuitiva que JSX. Además, por cada elemento React que deseemos crear, deberemos emplear la función React.createElement()
, lo cual puede resultar tedioso y disminuir la legibilidad del código.
En este caso, el componente Task
debe emplear la función React.createElement()
para crear el elemento <div>
, y para cada elemento hijo del elemento <div>
(en este caso, los elementos <h2>
y <p>
). Así para cada elemento React que deseemos aniadir.
Podemos ver una clara diferencia entre emplear JSX y emplear la API de React para definir componentes, queremos resaltar que esta alternativa no es recomendada, dado que es más compleja y menos intuitiva que JSX y, por lo tanto, disminuye la legibilidad del código.
Notaremos que la finalidad de JSX es facilitar la definición de componentes en React, centrando nuestra atención cómo se verá la interfaz de usuario y no en cómo se crean los elementos React.
Por estas razones, recomendamos emplear JSX para definir componentes en React y no emplear la API de React, ni trabajar con archivos JS.
Reglas de JSX para definir componentes
Como mencionamos anteriormente, JSX es una extensión de JavaScript que nos permite definir componentes en React de manera similar a como definimos elementos HTML. Sin embargo, como estamos trabajando con JavaScript, existen algunas reglas que debemos seguir para definir componentes en React con JSX.
Para explicar estas reglas, emplearemos nuevamente el componente Task
que definimos anteriormente.
Atributos
Los elementos React, al igual que cualquier elemento HTML, pueden tener atributos.
Como sabemos, los atributos son pares de nombre-valor que se especifican en la etiqueta de apertura de un elemento. Por ejemplo, el elemento <a>
tiene el atributo href
que especifica la URL a la que enlaza el elemento.
1<a href="https://www.google.com">Ir a Google</a>
No obstante, existen algunos atributos que podrían generar un conflicto con palabras reservadas de JavaScript. Por ejemplo, el atributo class
podría generar un conflicto con la palabra reservada class
de JavaScript (que se emplea para definir clases).
Por esta razón, en JSX se emplea la convención de nombres camelCase para especificar los nombres de los atributos. Por ejemplo, en lugar de emplear el atributo class
, se emplea el atributo className
.
1<div className="container">2 ...3</div>
De igual manera, en lugar de emplear el atributo for
, se emplea el atributo htmlFor
. Sumamente importante cuando definimos la etiqueta <label>
y queremos asociarla a un elemento <input>
.
1<label htmlFor="email">2 <input id="email" type="email" />3 Email4</label>
Así, debemos emplear la convención de nombres camelCase para especificar los nombres de los atributos en JSX que tengan dos o más palabras (por ejemplo, onChange
, onClick
, accessKey
, tabIndex
, etc.) o que podrían generar un conflicto con palabras reservadas de JavaScript (por ejemplo, className
, htmlFor
, etc.). Pueden encontrar más información sobre los atributos en JSX en la documentación oficial.
Existen dos excepciones a esta regla son data-*
attributes y aria-*
attributes. Los atributos que comienzan con data-
o aria-
mantienen su nombre en JSX. Por ejemplo, el atributo data-id
se especifica como data-id
en JSX.
Ahora podríamos agregar un atributo className
al elemento <div>
del componente Task
para aplicar estilos CSS al elemento resultante. Posteriormente veremos cómo trabajar con estilos CSS en React.
1function Task() {2 return (3 <div className="task">4 <h2>Comprar alimento para gatos</h2>5 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>6 </div>7 );8}
Etiqueta de cierre
Todo elemento React debe tener una etiqueta de cierre, a diferencia de los elementos HTML donde existen elementos que pueden no tener una etiqueta de cierre (por ejemplo, <br>
, <img>
, <input>
, etc.).
Para estos casos JSX establece dos soluciones:
- Agrega soporte para las etiquetas de cierre implícitas. Por ejemplo, el elemento
<br>
se puede escribir como<br />
en JSX. - Permite emplear la etiqueta de cierre explícita. Por ejemplo, el elemento
<br>
se puede escribir como<br></br>
en JSX.
Comentarios
Los comentarios en JSX se escriben como en JavaScript, empleando //
para comentarios de una línea y /* */
para comentarios de múltiples líneas.
1// Comentario de una línea2
3/*4 Comentario5 de6 múltiples7 líneas8*/
Sin embargo, ahora que podemos devolver elementos React en nuestros componentes, podemos emplear comentarios en JSX de la siguiente forma:
1function Task() {2 return (3 <div>4 {/* Título de la tarea */}5 <h2>Comprar alimento para gatos</h2>6 {/* Descripción de la tarea */}7 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>8 {/* <img src="..." alt="..." /> */}9 </div>10 );11}
Como podemos apreciar, al utilizar placeholders en JSX, podemos emplear comentarios con la sintaxis {/* Comentario */}
para documentar nuestro código, o incluso para comentar código que no deseamos que se ejecute. Esto último es muy útil cuando estamos desarrollando una aplicación web y queremos comentar código para probar diferentes alternativas.
Elementos retornados
Como sabemos, los componentes de React devuelven elementos React que describen lo que debe se renderizado en la pantalla.
Pero un hecho importante es que cada componente de React debe devolver un único elemento React. Por ejemplo, el componente Task
devuelve un único elemento <div>
. Esto se debe a que React no puede renderizar dos elementos hermanos (elementos que se encuentran en el mismo nivel de jerarquía) sin un elemento padre. Por lo cual sería incorrecto intentar devolver el <h2>
y el <p>
sin un elemento padre.
1function Task() {2 return (3 <h2>Comprar alimento para gatos</h2>4 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>5 );6}
Este componente produciría un error al intentar renderizarlo.
Estilos en línea
Si no importamos los estilos CSS dentro de un componente, podemos emplear estilos en línea para aplicar estilos CSS a los elementos React. Pero estos no emplean la misma sintaxis que suele usarse en HTML.
En JSX, los estilos en línea se especifican como objetos JavaScript. Retomando el ejemplo del componente Task
, podemos agregar estilos en línea al <h2>
que representa el título de la tarea. Supongamos que queremos aplicar un color de fuente rojo al título de la tarea y un tamaño de fuente de 1.5 rem. En este caso, podemos agregar un atributo style
al elemento <h2>
y especificar el color de fuente como un objeto JavaScript.
1function Task() {2 return (3 <div className="task">4 <h2 style={{ color: 'red', fontSize: '1.5rem' }}>Comprar alimento para gatos</h2>5 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>6 </div>7 );8}
Comparándolo con HTML, donde tendriámos que definir una cadena de texto con los estilos CSS.
1<h2 style="color: red;">Comprar alimento para gatos</h2>
Ante algunas situaciones, los estilos en línea pueden ser útiles. Sin embargo, en la mayoría de los casos, es recomendable importar estilos CSS externos. En caso de que necesitemos aplicar estilos en línea, recomendamos realizar el siguiente procedimiento:
- Definir un objeto JavaScript previamente con los estilos CSS que deseamos aplicar.
1const styles = {2 color: 'red',3 fontSize: '1.5rem',4};
- Agregar un atributo
style
al elemento React y especificar el objeto JavaScript con los estilos CSS.
1function Task() {2 return (3 <div className="task">4 <h2 style={styles}>Comprar alimento para gatos</h2>5 <p>Comprar alimento para gatos en el super, marca Whiskas con sabor a salmón</p>6 </div>7 );8}
Esto garantizará un código más legible y mantenible, pero una vez más, recomendamos importar estilos CSS externos.