Saltearse al contenido

Eventos

En cualquier aplicación web necesitaremos que los elementos de la página sean reactivos a las acciones del usuario (de ahí el nombre React).

Convencionalmente, en HTML y empleando JavaScript, se utilizan los eventos para reaccionar a las acciones del usuario. Recordaremos que, en la necesidad de responder a estos eventos, empleabamos métodos como addEventListener en los cuales definíamos una función que se ejecutaría cuando se produjera el evento. No obstante, React nos permite añadir manejadores de eventos a JSX.

Manejadores de eventos

Un manejador de evento es una función que se ejecutará en respuesta a ciertas interacciones (los llamados eventos) como hacer click en un botón, pasar el ratón por encima de un elemento (hover), enfocar un campo de texto, entre otros.

Para agregar un manejador de evento, primero debemos definir una función, recomendablemente dentro del componente que lo usará. Luego, debemos pasar la función como valor de una propiedad a la etiqueta JSX que queremos que reaccione al evento. Por ejemplo, si contamos con el siguiente componente:

1
function AlertButton() {
2
return <button>¡Haz click!</button>;
3
}

Veremos que no ocurre nada al hacer click en el botón. Para que esto ocurra, definiremos la función handleClick y la pasaremos como valor de la propiedad onClick de la etiqueta button:

1
function AlertButton() {
2
function handleClick() {
3
alert('¡Haz hecho click!');
4
}
5
6
return <button onClick={handleClick}>¡Haz click!</button>;
7
}

Por convención, los manejadores de eventos se nombran con el prefijo handle seguido del nombre del evento que manejan. En este caso, el evento es click, por lo que el manejador se llama handleClick.

Por otro lado, podemos definir un manejar de evento directamente en la etiqueta JSX, ya sea usando una función anónima (o función flecha):

1
function AlertButton() {
2
return <button onClick={() => alert('¡Haz hecho click!')}>¡Haz click!</button>;
3
}

Esta alternativa es útil cuando la función es muy corta y no queremos definirla por separado. No obstante, si la función tiene más de una línea, es recomendable definirla por separado para no comprometer la legibilidad del código.

Atención: otro detalle de suma importancia al momento establecer un manejador de evento es que no debemos ejecutar la función. Es decir, no debemos usar paréntesis () al final del nombre de la función. Si lo hacemos, la función se ejecutará inmediatamente al momento de renderizar el componente, en lugar de esperar a que se produzca el evento.

Podemos ver una comparativa de ambos casos en la siguiente tabla:

IncorrectoCorrecto
<button onClick={handleClick()}><button onClick={handleClick}>

De igual manera, debemos respetar esta sintaxis al momento de definir un manejador de evento directamente en la etiqueta JSX:

IncorrectoCorrecto
<button onClick={alert('¡Haz hecho click!')}><button onClick={() => alert('¡Haz hecho click!')}>

Nótense los paréntesis al momento de definir la función anónima (parametros) => { ... }.

Propiedades en manejadores de eventos

Una clara ventaja al declarar un manejador de evento dentro de un componente, es que podemos acceder a las propiedades del componente.

Por ejemplo, podríamos modificar el componente AlertButton para que muestre un mensaje personalizado al hacer click en el botón. Para ello, agregaremos una propiedad message al componente:

1
function AlertButton({ message }) {
2
function handleClick() {
3
alert(message);
4
}
5
6
return <button onClick={handleClick}>¡Haz click!</button>;
7
}
8
9
export default AlertButton;

Como vemos, la función handleClick puede acceder a la propiedad message del componente. De esta manera, podemos reutilizar el componente AlertButton para mostrar distintos mensajes:

1
function App() {
2
return (
3
<div>
4
<AlertButton message="Primer botón" />
5
<AlertButton message="Segundo botón" />
6
</div>
7
);
8
}

Manejadores como propiedades

En ocasiones podríamos querer indicar a un componente cual es el comportamiento que debe tener al producirse un evento. Dada esta situación, podemos pasar un manejador de evento como propiedad al componente (al igual que lo hacemos con otras propiedades).

Aplicando esta metodología, podemos especializar los componentes a partir de otros componentes más genéricos. Por ejemplo, podríamos redefinir el componente AlertButton para que reciba un manejador de evento como propiedad y elementos hijos:

1
function AlertButton({ handleClick, children }) {
2
return <button onClick={handleClick}>{children}</button>;
3
}
4
5
export default AlertButton;

De esta manera, podemos reutilizar el componente AlertButton y crear otros componentes más especializados:

1
function WarningButton({ message }) {
2
function handleClick() {
3
alert("Advertencia: " + message);
4
}
5
6
return <AlertButton handleClick={handleClick}>¡Haz click!</AlertButton>;
7
}
8
9
export default WarningButton;
1
function ErrorButton() {
2
function handleClick() {
3
alert("Error: no se puede realizar la acción");
4
}
5
6
return <AlertButton handleClick={handleClick}>¡Haz click!</AlertButton>;
7
}

Así mismo podríamos crear tantos botones con comportamientos distintos como necesitemos, siempre y cuando definamos el manejador de evento correspondiente. Luego, podemos usarlos en nuestra aplicación:

1
function App() {
2
return (
3
<div>
4
<WarningButton message="este es un mensaje de advertencia" />
5
<ErrorButton/>
6
</div>
7
);
8
}

Puede que este ejemplo, sea un poco rebuscado, pero nos permite ver como podemos minimizar la cantidad de código que utilizamos para definir una aplicación. Además, de permitirnos reutilizar componentes y diferenicarlos de manera clara y concisa.

Como dato de color, podemos mencionar que es una práctica común encontrar componentes que reciben un manejador de evento como propiedad, para los cuales se emplea cierta notación. Para esto se emplea el prefijo on seguido del nombre del evento. Por ejemplo, si tenemos un componente que recibe un manejador de evento handleClick, la propiedad se llamará onClickButton. Nótese que el nombre del evento no debe llamarse igual a otro evento que ya exista en HTML, razón por la cual se emplea la notación onClickButton en lugar de onClick. Esto puede resultarnos útil si queremos definir nuestro propio evento o si queremos utilizar un componente definido por un tercero. Por ejemplo, podría existir un componente Button que recibe un manejador de evento onSmash:

1
function Button({ onSmash }) {
2
return <button onClick={onSmash}>¡Haz click!</button>;
3
}

Para usarlo, deberemos definir el manejador de evento y pasarlo como atributo al componente:

1
function App() {
2
function handleSmash() {
3
alert("¡Smash!");
4
}
5
6
return <Button onSmash={handleSmash} />;
7
}

Propagación de eventos

Existe una característica que debemos tener presente siempre que definamos manejadores de eventos, y es que los eventos se propagan (o se expanden) hacia los elementos padres. Esto quiere decir que, si tenemos un elemento que contiene otros elementos, al producirse un evento en un elemento hijo, el evento se producirá también en el elemento padre.

Para entender mejor este concepto, y como puede afectar a nuestra aplicación, podemos ver el siguiente ejemplo. En este caso, tenemos un componente App que contiene un componente AlertButton:

1
function AlertButton({ message }) {
2
function handleClick() {
3
alert(message);
4
}
5
6
return <button onClick={handleClick}>¡Haz click!</button>;
7
}

Además del evento onClick del botón, el componente App también tiene un manejador de evento onClick en una etiqueta div:

1
function App() {
2
function handleClick() {
3
alert("¡Haz hecho click en el contenedor de la aplicación!");
4
}
5
6
return (
7
<div>
8
<div style={{ border: "1px solid black" }} onClick={handleClick}>
9
<AlertButton message="Primer botón" />
10
<AlertButton message="Segundo botón" />
11
12
</div>
13
<AlertButton message="Tercer botón" />
14
</div>
15
);
16
}

Ahora, si hacemos click en uno de los botones contenidos en el elemento div que tiene estilos, veremos que se ejecutan tanto el manejador de eventos del botón como del elemento div. Pero, si hacemos click en el botón que está fuera del elemento div con estilos (el tercer botón), veremos que solo se ejecuta el manejador de eventos del botón.

Esto nos demuestra que para un mismo evento, se ejecutan todos los manejadores de eventos de manera recursiva desde el elemento que lo produjo hasta el elemento raíz de la aplicación (siempre y cuando se hayan definido dichos manejadores de eventos).

Esta característica no siempre será perjudicial para nosotros, de hecho puede ser muy útil en ciertas ocasiones. No obstante, debemos tenerla presente para evitar comportamientos inesperados en nuestra aplicación.

Atención: todos los eventos se propagan en React, excepto el evento onScroll. El cual sólo funciona en el elemento al que se le asigna.

Detener la propagación de eventos

Los manejadores de eventos pueden recibir un objeto de evento como único parámetro. Por convención, normalmente es llamado e, que quiere decir evento. Puedemos usar este objeto para leer información del evento y manipularlo a nuestro gusto.

Mediante este objeto, podemos detener la propagación de eventos. Para ello, debemos usar el método stopPropagation del objeto de evento. Por ejemplo, si queremos que el evento onClick de un botón no se propague, podemos definir el manejador de evento de la siguiente manera:

1
function handleClick(e) {
2
e.stopPropagation();
3
alert("¡Haz hecho click!");
4
}

De esta manera, al hacer click en el botón, se ejecutará el manejador de evento del botón, pero no se ejecutará el manejador de evento del elemento padre. Puedes modificar el componente AlertButton que definimos anteriormente para ver este comportamiento.

Además de detener la propagación de eventos, podemos usar el objeto de evento para leer información del evento. Por ejemplo, podemos leer la posición del mouse al momento de hacer click en un botón:

1
function handleClick(e) {
2
alert(`Haz hecho click en la posición (${e.clientX}, ${e.clientY})`);
3
}

De igual manera, podemos leer el elemento que produjo el evento, así como la tecla que se presionó, entre otras propiedades.

Evitar comportamientos por defecto

Algunos eventos del navegador tienen comportamiento por defecto asociados a ellos. Por ejemplo, si tenemos un elemento <button> dentro de un formulario (<form>), al hacer click en el botón se recargará la página. Esto se debe a que el evento onClick del botón tiene un comportamiento por defecto que es enviar el formulario.

Para evitar este comportamiento, podemos usar el objeto de evento para llamar al método preventDefault.

1
function handleClick(e) {
2
e.preventDefault();
3
alert("¡Haz hecho click!");
4
}

Así para cualquier evento que tenga un comportamiento por defecto.

Atención: no confundir los métodos e.stopPropagation() y e.preventDefault().

  • El método e.stopPropagation() detiene la propagación de eventos.
  • El método e.preventDefault() evita el comportamiento por defecto del evento.

Eventos que podemos manejar

A continuación, veremos una tabla con los eventos más comunes que podemos manejar en React:

TipoEventoDescripción
Clipboard eventonCopyDisparado al copiar texto al portapapeles.
Clipboard eventonCutDisparado al cortar texto al portapapeles.
Clipboard eventonPasteDisparado al pegar texto del portapapeles.
Keyboard eventonKeyDownDisparado cuando se presiona una tecla.
Keyboard eventonKeyPressDisparado cuando se presiona y suelta una tecla.
Keyboard eventonKeyUpDisparado cuando se suelta una tecla presionada.
FocusonFocusDisparado cuando un elemento recibe el foco.
FocusonBlurDisparado cuando un elemento pierde el foco.
Form eventonChangeDisparado cuando el valor de un elemento de formulario cambia.
Form eventonInputDisparado cuando se ingresa información en un campo de texto.
Form eventonInvalidDisparado cuando un elemento de formulario es inválido.
Form eventonResetDisparado al resetear un formulario.
Form eventonSubmitDisparado al enviar un formulario.
Generic eventsonErrorDisparado cuando ocurre un error durante la carga de un recurso.
Generic eventsonLoadDisparado cuando un recurso ha terminado de cargar.
Mouse eventonClickDisparado al hacer clic con el ratón.
Mouse eventonDoubleClickDisparado al hacer doble clic con el ratón.
Mouse event
Mouse eventonMouseDownDisparado al presionar un botón del mouse sobre un elemento (sin soltarlo).
Mouse eventonMouseUpDisparado al soltar un botón del mouse sobre un elemento.
Mouse eventonMouseDragDisparado al arrastrar un elemento.
Mouse eventonMouseEnterDisparado al pasar el mouse por encima de un elemento.
Mouse eventonMouseLeaveDisparado al sacar el mouse de un elemento.
Selection eventonSelectDisparado al seleccionar un texto.
UI eventonScrollDisparado al hacer scroll en un elemento.
Image eventonLoadDisparado cuando una imagen ha terminado de cargar.
Image eventonErrorDisparado cuando ocurre un error al cargar una imagen.
Animation eventonAnimationStartDisparado al iniciar una animación.
Animation eventonAnimationEndDisparado al finalizar una animación.
Animation eventonAnimationIterationDisparado al repetirse una animación.

Entre otros muchos eventos…

Recomendamos consultar la documentación de cada evento si buscamos realizar una acción que escapa a las mencionadas en la tabla anterior (lista completa o tabla resumida).

Bibliografía