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:
1function 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
:
1function 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):
1function 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:
Incorrecto | Correcto |
---|---|
<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:
Incorrecto | Correcto |
---|---|
<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:
1function AlertButton({ message }) {2 function handleClick() {3 alert(message);4 }5
6 return <button onClick={handleClick}>¡Haz click!</button>;7}8
9export 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:
1function 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:
1function AlertButton({ handleClick, children }) {2 return <button onClick={handleClick}>{children}</button>;3}4
5export default AlertButton;
De esta manera, podemos reutilizar el componente AlertButton
y crear otros componentes más especializados:
1function WarningButton({ message }) {2 function handleClick() {3 alert("Advertencia: " + message);4 }5
6 return <AlertButton handleClick={handleClick}>¡Haz click!</AlertButton>;7}8
9export default WarningButton;
1function 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:
1function 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
:
1function 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:
1function 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
:
1function 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
:
1function 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:
1function 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:
1function 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
.
1function 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()
ye.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:
Tipo | Evento | Descripción |
---|---|---|
Clipboard event | onCopy | Disparado al copiar texto al portapapeles. |
Clipboard event | onCut | Disparado al cortar texto al portapapeles. |
Clipboard event | onPaste | Disparado al pegar texto del portapapeles. |
Keyboard event | onKeyDown | Disparado cuando se presiona una tecla. |
Keyboard event | onKeyPress | Disparado cuando se presiona y suelta una tecla. |
Keyboard event | onKeyUp | Disparado cuando se suelta una tecla presionada. |
Focus | onFocus | Disparado cuando un elemento recibe el foco. |
Focus | onBlur | Disparado cuando un elemento pierde el foco. |
Form event | onChange | Disparado cuando el valor de un elemento de formulario cambia. |
Form event | onInput | Disparado cuando se ingresa información en un campo de texto. |
Form event | onInvalid | Disparado cuando un elemento de formulario es inválido. |
Form event | onReset | Disparado al resetear un formulario. |
Form event | onSubmit | Disparado al enviar un formulario. |
Generic events | onError | Disparado cuando ocurre un error durante la carga de un recurso. |
Generic events | onLoad | Disparado cuando un recurso ha terminado de cargar. |
Mouse event | onClick | Disparado al hacer clic con el ratón. |
Mouse event | onDoubleClick | Disparado al hacer doble clic con el ratón. |
Mouse event | ||
Mouse event | onMouseDown | Disparado al presionar un botón del mouse sobre un elemento (sin soltarlo). |
Mouse event | onMouseUp | Disparado al soltar un botón del mouse sobre un elemento. |
Mouse event | onMouseDrag | Disparado al arrastrar un elemento. |
Mouse event | onMouseEnter | Disparado al pasar el mouse por encima de un elemento. |
Mouse event | onMouseLeave | Disparado al sacar el mouse de un elemento. |
Selection event | onSelect | Disparado al seleccionar un texto. |
UI event | onScroll | Disparado al hacer scroll en un elemento. |
Image event | onLoad | Disparado cuando una imagen ha terminado de cargar. |
Image event | onError | Disparado cuando ocurre un error al cargar una imagen. |
Animation event | onAnimationStart | Disparado al iniciar una animación. |
Animation event | onAnimationEnd | Disparado al finalizar una animación. |
Animation event | onAnimationIteration | Disparado 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).