Eventos en React

Hasta ahora nuestros componentes son estáticos: muestran información pero no responden a las acciones del usuario. En esta clase vamos a aprender a manejar eventos como clicks, cambios en inputs, envío de formularios, etc.

¿Qué son los eventos?

Los eventos son acciones que ocurren en el navegador:

  • 👆 Click - El usuario hace clic en un botón
  • ⌨️ Input - El usuario escribe en un input
  • 📝 Change - El valor de un input cambia
  • 📤 Submit - Se envía un formulario
  • 🖱️ Hover - El ratón pasa sobre un elemento

React nos permite escuchar estos eventos y ejecutar código cuando ocurren.

Añadiendo eventos onClick

Vamos a hacer que el botón “Aplicar” responda a clicks. Todavía sin estado, solo vamos a mostrar un mensaje en la consola y una alerta.

Actualiza tu archivo react.html con este código:

function JobCard({ titulo, empresa, ubicacion, descripcion }) {
  // Función que se ejecuta al hacer click
  const handleAplicar = () => {
    console.log('¡Click en el botón!')
    console.log('Aplicando a:', titulo, '-', empresa)
    alert(`Has aplicado a: ${titulo} en ${empresa}`)
  }

  return (
    <article className="job-listing-card">
      <div>
        <h3>{titulo}</h3>
        <small>
          {empresa} | {ubicacion}
        </small>
        <p>{descripcion}</p>
      </div>
      <button className="button-apply-job" onClick={handleAplicar}>
        Aplicar
      </button>
    </article>
  )
}

¿Qué cambió?

Analicemos las novedades paso a paso.

1. Función manejadora de eventos

function JobCard({ titulo, empresa, ubicacion, descripcion }) {
  // Definimos una función que se ejecuta al hacer click
  const handleAplicar = () => {
    console.log('¡Click en el botón!')
    console.log('Aplicando a:', titulo, '-', empresa)
    alert(`Has aplicado a: ${titulo} en ${empresa}`)
  }

  return (
    <article className="job-listing-card">
      {/* ... */}
      <button className="button-apply-job" onClick={handleAplicar}>
        Aplicar
      </button>
    </article>
  )
}

Puntos clave:

  1. Definimos una función handleAplicar dentro del componente
  2. Esta función tiene acceso a las props (titulo, empresa)
  3. Conectamos la función al botón con onClick={handleAplicar}

2. Sintaxis de eventos en React

<button onClick={handleAplicar}>Aplicar</button>

Importante:

  • ✅ Usa camelCase: onClick, no onclick
  • ✅ Pasa la referencia a la función: onClick={handleAplicar}
  • NO la llames: onClick={handleAplicar()} ← Esto ejecuta la función inmediatamente
// ✅ Correcto: pasa la referencia
<button onClick={handleAplicar}>Aplicar</button>

// ❌ Incorrecto: ejecuta la función inmediatamente
<button onClick={handleAplicar()}>Aplicar</button>

// ✅ Alternativa con función inline
<button onClick={() => alert('Click!')}>Aplicar</button>

Pruébalo

  1. Abre el archivo en tu navegador
  2. Abre la consola (F12 o Cmd+Option+I en Mac)
  3. Haz click en cualquier botón “Aplicar”
  4. Verás los mensajes en la consola y una alerta

Convención de nombres

Por convención, las funciones manejadoras de eventos suelen empezar con handle:

const handleClick = () => {
  /* ... */
}
const handleSubmit = () => {
  /* ... */
}
const handleChange = () => {
  /* ... */
}
const handleAplicar = () => {
  /* ... */
}

Esto hace el código más legible: cuando ves handleAlgo, sabes que es un manejador de eventos.

Comparación con JavaScript vanilla

Veamos cómo hemos simplificado el manejo de eventos.

Antes (JavaScript vanilla)

En nuestro archivo apply-button.js teníamos que hacer esto:

const jobsListingSection = document.querySelector('.jobs-listings')

jobsListingSection.addEventListener('click', function (event) {
  const element = event.target
  if (element.classList.contains('button-apply-job')) {
    element.textContent = '¡Aplicado!'
    element.classList.add('is-applied')
    element.disabled = true
  }
})

Problemas:

  • Event delegation - Tenemos que escuchar en un contenedor padre
  • Verificar el target - Con if (element.classList.contains(...))
  • Manipular el DOM manualmente - Cambiar texto, clases, etc.
  • Código difícil de mantener - Todo mezclado

Ahora (React)

const handleAplicar = () => {
  alert(`Has aplicado a: ${titulo} en ${empresa}`)
}

;<button onClick={handleAplicar}>Aplicar</button>

Ventajas:

  • Directo - El evento está donde lo necesitas
  • Sin querySelector - No buscas elementos
  • Sin addEventListener - React lo hace por ti
  • Sin event delegation - Cada componente gestiona sus eventos
  • Acceso a props - La función tiene acceso al contexto del componente

¡Mucho más simple!

Eventos con funciones inline

A veces es útil definir la función directamente en el onClick:

function JobCard({ titulo, empresa }) {
  return <button onClick={() => alert(`Aplicando a ${titulo}`)}>Aplicar</button>
}

¿Cuándo usar cada enfoque?

Función separada (recomendado)

function JobCard({ titulo }) {
  const handleClick = () => {
    console.log('Click!')
    alert(titulo)
  }

  return <button onClick={handleClick}>Aplicar</button>
}

Mejor cuando:

  • La función tiene más de una línea
  • Quieres reutilizar la función
  • Necesitas testear la función

Función inline

<button onClick={() => alert('Click!')}>Aplicar</button>

Mejor cuando:

  • Es una sola línea muy simple
  • No se va a reutilizar

Acceso a las props en los manejadores

Los manejadores de eventos tienen acceso completo a las props del componente:

function JobCard({ titulo, empresa, ubicacion, descripcion }) {
  const handleAplicar = () => {
    // Podemos usar todas las props
    console.log('Título:', titulo)
    console.log('Empresa:', empresa)
    console.log('Ubicación:', ubicacion)
    console.log('Descripción:', descripcion)

    alert(`Has aplicado a ${titulo} en ${empresa}`)
  }

  return (
    <article>
      <h3>{titulo}</h3>
      <button onClick={handleAplicar}>Aplicar</button>
    </article>
  )
}

Esto es posible gracias a los closures de JavaScript: la función handleAplicar “recuerda” las variables del ámbito donde fue creada.

Tipos de eventos comunes

React soporta todos los eventos del navegador. Los más comunes:

onClick - Click del ratón

<button onClick={() => console.log('Click!')}>Haz click</button>

onChange - Cambio en un input

<input onChange={(e) => console.log('Valor:', e.target.value)} />

onSubmit - Envío de formulario

<form
  onSubmit={(e) => {
    e.preventDefault()
    console.log('Formulario enviado')
  }}
>
  <button type="submit">Enviar</button>
</form>

onMouseEnter / onMouseLeave - Hover

<div onMouseEnter={() => console.log('Entrando')} onMouseLeave={() => console.log('Saliendo')}>
  Pasa el ratón
</div>

onKeyDown / onKeyUp - Teclas

<input onKeyDown={(e) => console.log('Tecla:', e.key)} />

El objeto evento (event)

Los manejadores de eventos reciben un objeto evento como parámetro:

function JobCard({ titulo }) {
  const handleAplicar = (event) => {
    console.log('Evento completo:', event)
    console.log('Elemento que disparó el evento:', event.target)
    console.log('Tipo de evento:', event.type)
  }

  return <button onClick={handleAplicar}>Aplicar</button>
}

Propiedades útiles del evento

const handleClick = (event) => {
  event.preventDefault() // Prevenir comportamiento por defecto
  event.stopPropagation() // Detener propagación del evento
  event.target // Elemento que disparó el evento
  event.currentTarget // Elemento al que está adjunto el evento
  event.type // Tipo de evento ('click', 'change', etc.)
}

Ejemplo práctico con preventDefault

function Formulario() {
  const handleSubmit = (event) => {
    event.preventDefault() // Evita recargar la página
    console.log('Formulario enviado')
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" placeholder="Nombre" />
      <button type="submit">Enviar</button>
    </form>
  )
}

Pasando argumentos a los manejadores

¿Qué pasa si necesitas pasar argumentos adicionales?

Opción 1: Función inline con arrow function

function JobCard({ titulo, id }) {
  const handleAplicar = (jobId) => {
    console.log('ID del empleo:', jobId)
  }

  return <button onClick={() => handleAplicar(id)}>Aplicar</button>
}

Opción 2: Con el evento

function JobCard({ titulo, id }) {
  const handleAplicar = (event, jobId) => {
    console.log('Evento:', event)
    console.log('ID del empleo:', jobId)
  }

  return <button onClick={(e) => handleAplicar(e, id)}>Aplicar</button>
}

Múltiples eventos en un componente

Puedes tener varios manejadores en el mismo componente:

function JobCard({ titulo, empresa }) {
  const handleAplicar = () => {
    alert(`Aplicando a ${titulo}`)
  }

  const handleGuardar = () => {
    alert(`Guardando ${titulo}`)
  }

  const handleCompartir = () => {
    alert(`Compartiendo ${titulo}`)
  }

  return (
    <article>
      <h3>{titulo}</h3>
      <button onClick={handleAplicar}>Aplicar</button>
      <button onClick={handleGuardar}>Guardar</button>
      <button onClick={handleCompartir}>Compartir</button>
    </article>
  )
}

Limitaciones actuales

Por ahora, cuando hacemos click en “Aplicar”:

  • ✅ Se ejecuta la función
  • ✅ Se muestra la alerta
  • ✅ Se imprime en consola
  • ❌ El botón NO cambia visualmente
  • ❌ No se recuerda que aplicaste

¿Por qué?

Porque todavía no estamos usando estado. El componente no “recuerda” nada entre renders.

En la próxima clase aprenderemos sobre useState, que nos permitirá:

  • Cambiar el texto del botón a “¡Aplicado!”
  • Desactivar el botón después de aplicar
  • Añadir una clase CSS
  • ¡Y mucho más!

Lo que hemos aprendido de los eventos de React

  • 🎯 Qué son los eventos en React
  • 👆 Sintaxis de eventos - onClick, onChange, etc.
  • 🔧 Crear manejadores - Funciones que responden a eventos
  • 📦 Acceso a props - Los manejadores pueden usar las props
  • Comparación con vanilla JS - Mucho más simple
  • 🎨 Diferentes tipos de eventos - Click, change, submit, hover, etc.
  • 📝 Objeto evento - Acceder a información del evento
  • 🔀 Múltiples eventos - Varios manejadores en un componente

En la próxima clase aprenderemos sobre el estado con useState, que nos permitirá crear componentes que cambian en respuesta a eventos.

💡 Recuerda: Los eventos en React usan camelCase (onClick) y reciben referencias a funciones, no llamadas (onClick={handleClick}, no onClick={handleClick()}).