CodeMIRAI
0 XP

Handling events

2 min read 6 runnable examples Quiz included

React handles events through props named like onClick and onChange, written in camelCase, and you give each one a function to call.

React (JSX)
import { useState } from "react";

function App() {
  const [message, setMessage] = useState("Nothing clicked yet");

  function handleClick() {
    setMessage("Thanks for clicking!");
    console.log("clicked at", new Date().toLocaleTimeString());
  }

  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <p>{message}</p>
    </div>
  );
}

onClick={handleClick} hands React the function itself, to call later. A common mistake is onClick={handleClick()}, with brackets: that calls the function immediately, during rendering, instead of on click.

Passing an argument

React (JSX)
import { useState } from "react";

function App() {
  const [choice, setChoice] = useState(null);
  const sizes = ["Small", "Medium", "Large"];

  return (
    <div>
      {sizes.map((size) => (
        <button key={size} onClick={() => setChoice(size)} style={{ marginRight: 6 }}>
          {size}
        </button>
      ))}
      <p>{choice ? `You picked ${choice}` : "Pick a size"}</p>
    </div>
  );
}

To pass an argument, wrap the call in an arrow function: onClick={() => setChoice(size)}. The arrow function is what React calls on a click, and it then calls setChoice with the right size.

The event object

React (JSX)
import { useState } from "react";

function App() {
  const [text, setText] = useState("");
  const [lastKey, setLastKey] = useState("");

  return (
    <div>
      <input
        value={text}
        placeholder="Type something"
        onChange={(e) => setText(e.target.value)}
        onKeyDown={(e) => setLastKey(e.key === " " ? "Space" : e.key)}
      />
      <p>You typed: {text}</p>
      <p>Last key: {lastKey}</p>
      <p>{text.length} characters</p>
    </div>
  );
}

React passes every handler an event object, usually called e. e.target is the element where the event happened, so e.target.value is an input's current text, and e.key names the key that was pressed.

Forms: preventDefault

React (JSX)
import { useState } from "react";

function App() {
  const [email, setEmail] = useState("");
  const [status, setStatus] = useState("");

  function handleSubmit(e) {
    e.preventDefault();              // stop the browser from reloading the page
    setStatus(`Subscribed ${email}`);
    setEmail("");
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" required value={email} onChange={(e) => setEmail(e.target.value)} placeholder="you@example.com" />{" "}
      <button>Subscribe</button>
      <p>{status}</p>
    </form>
  );
}

Handle onSubmit on the form rather than onClick on the button, so pressing Enter works too. e.preventDefault() cancels the browser's default action, which for a form is sending it and reloading the page.

Mouse events

React (JSX)
import { useState } from "react";

function App() {
  const [pos, setPos] = useState({ x: 0, y: 0 });
  const [inside, setInside] = useState(false);

  return (
    <div
      onMouseMove={(e) => {
        const box = e.currentTarget.getBoundingClientRect();
        setPos({ x: Math.round(e.clientX - box.left), y: Math.round(e.clientY - box.top) });
      }}
      onMouseEnter={() => setInside(true)}
      onMouseLeave={() => setInside(false)}
      style={{ height: 150, borderRadius: 12, display: "grid", placeItems: "center",
               background: inside ? "#efedff" : "#f3f4f8" }}
    >
      {inside ? `x: ${pos.x}, y: ${pos.y}` : "Move your mouse in here"}
    </div>
  );
}

e.currentTarget is the element the handler is attached to, while e.target may be a child inside it.

Stopping an event from bubbling up

React (JSX)
import { useState } from "react";

function App() {
  const [log, setLog] = useState([]);
  const add = (msg) => setLog((old) => [...old, msg].slice(-4));

  return (
    <div>
      <div onClick={() => add("card clicked")} style={{ padding: 16, border: "1px solid #ccd", borderRadius: 10 }}>
        <strong>A card</strong>
        <p>Click anywhere on the card, then click Delete.</p>
        <button onClick={(e) => { e.stopPropagation(); add("delete clicked, and only delete"); }}>Delete</button>
      </div>
      <ul>{log.map((msg, i) => <li key={i}>{msg}</li>)}</ul>
    </div>
  );
}

Events bubble: a click on the button is also a click on the card around it, so both handlers would run. e.stopPropagation() stops the event before it reaches the card.

Finished this lesson?Mark it complete to earn +20 XP and move your progress on.