Saltearse al contenido

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.

1
function 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>.

1
class 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.

Componente Navbar Figura 2.3: 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.

Componente NavbarItem Figura 2.4: Componente 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 denominaremos Navbar.

Componente Navbar Figura 2.5: Componente 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.

Componente PostList Figura 2.6: 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.

Componente Navbar Figura 2.7: Componente Navbar. Barra de navegación en la sección de inicio de la aplicación web de Twitter.

Componente Navbar Figura 2.8: Componente 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:

Diagrama de componentes

En este diagrama, podemos identificar los siguientes componentes:

  • Task: los componentes Task se utilizan para representar cada tarea en la lista de tareas. Cada componente Task tiene un título y una descripción.

Componente Task

  • TaskList: el componente TaskList se utiliza para representar la lista de tareas. Este componente tiene un título y una lista de tareas.

Componente TaskList

  • App: el componente App se utiliza para agrupar todos los componentes de una sección de la aplicación web. En este caso, el componente App agrupa el componente TaskList, pero siempre podemos agregar más componentes si es necesario (por ejemplo, un componente Navbar).

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:

1
function App() {
2
return (
3
<div>
4
<TaskList />
5
</div>
6
);
7
}
8
9
export 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:

1
function 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:

1
function 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:

1
function 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:

1
import React from 'react';
2
3
function 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:

  1. El tipo de elemento React que se desea crear (en este caso, un elemento <div>).
  2. Un objeto con las propiedades del elemento React (en este caso, no se especifican propiedades).
  3. 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 especificar null, undefined o false. 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
Email
4
</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.

1
function 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:

  1. Agrega soporte para las etiquetas de cierre implícitas. Por ejemplo, el elemento <br> se puede escribir como <br /> en JSX.
  2. 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ínea
2
3
/*
4
Comentario
5
de
6
múltiples
7
líneas
8
*/

Sin embargo, ahora que podemos devolver elementos React en nuestros componentes, podemos emplear comentarios en JSX de la siguiente forma:

1
function 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.

1
function 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.

1
function 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:

  1. Definir un objeto JavaScript previamente con los estilos CSS que deseamos aplicar.
1
const styles = {
2
color: 'red',
3
fontSize: '1.5rem',
4
};
  1. Agregar un atributo style al elemento React y especificar el objeto JavaScript con los estilos CSS.
1
function 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.

Bibliografía