Handling Events in React: Clicks, Typing and Forms Made Simple

React.js Full Course: Beginner to Advanced · Start Here · 9 min read · Updated

An event travels up

  1. The person clicks the Like button.
  2. The click event reaches the onClick handler of the button.
  3. The child calls the onLike function that its parent gave it as a prop.
  4. The parent changes its own state with a new value.
  5. React draws again and the screen shows one more like. Remember: pass the function, do not call it.

A page that never reacts is just a poster. Events are how people talk to your app: they click, type, hover, press keys and submit forms. In React, handling events is simple and looks like HTML, with a few small differences. This lesson shows each of them.

Your first event handler

To react to a click, give the element an onClick prop. Its value is the function to run when the click happens.

function Hello() {
  function handleClick() {
    alert('You clicked the button!');
  }

  return <button onClick={handleClick}>Say hello</button>;
}

A good habit is to start the name of the handler with handle, such as handleClick or handleSubmit. It makes your code easy to read.

The most common mistake: calling the function

Look at the difference between these two lines:

<button onClick={handleClick}>Right</button>      {/* passes the function */}
<button onClick={handleClick()}>Wrong</button>    {/* runs it at once */}

The wrong version runs handleClick() while the page is being drawn, and passes its result to the button. Remember: pass the function, do not call it. Without brackets you give React the function to call later. With brackets you call it now.

Inline handlers

For very short actions, you can write the function directly. An arrow function is the usual way:

<button onClick={() => setCount(count + 1)}>Add</button>

If your handler is longer than one line, write a named function above, so the JSX stays easy to read.

Passing an argument

Often you need to tell the handler which item was clicked. Wrap the call in an arrow function:

function List({ items, onRemove }) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>
          {item.name}
          <button onClick={() => onRemove(item.id)}>Remove</button>
        </li>
      ))}
    </ul>
  );
}

The arrow function () => onRemove(item.id) waits until the click, then calls onRemove with the right id. Each button gets its own arrow function, so each remembers its own item.

The event object

React gives your handler one argument: the event object. It holds details about what happened. The most useful parts are:

  • event.target: the element that caused the event. For an input, event.target.value is the text in it.
  • event.preventDefault(): stops the browser's normal action, such as reloading the page when a form is submitted.
  • event.key: for key events, the key that was pressed, such as 'Enter'.
  • event.stopPropagation(): stops the event from reaching parent elements.
function Search() {
  const [text, setText] = useState('');

  function handleChange(event) {
    setText(event.target.value);
  }

  function handleKeyDown(event) {
    if (event.key === 'Enter') {
      console.log('Searching for', text);
    }
  }

  return <input value={text} onChange={handleChange} onKeyDown={handleKeyDown} />;
}

Form submit

A form that is submitted makes the browser reload the page, which would throw away your state. Stop that with preventDefault:

function SignUp() {
  const [email, setEmail] = useState('');

  function handleSubmit(event) {
    event.preventDefault();
    console.log('Sending', email);
  }

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" value={email} onChange={(e) => setEmail(e.target.value)} />
      <button type="submit">Join</button>
    </form>
  );
}

Use onSubmit on the form, not onClick on the button. That way the form also works when the person presses Enter, and it works with screen readers and keyboards.

Common events

  • onClick: a click or tap.
  • onChange: the value of an input changed. In React it fires on every key press.
  • onSubmit: a form was submitted.
  • onKeyDown and onKeyUp: a key was pressed or released.
  • onMouseEnter and onMouseLeave: the pointer moved onto or off an element.
  • onFocus and onBlur: an element got or lost focus.

Events travel up (bubbling)

When you click a button inside a box, the click first happens on the button, then bubbles up to the box and then to its parents. If both have an onClick, both run. If you do not want the parent to know, call event.stopPropagation() in the child.

<div onClick={() => console.log('box clicked')}>
  <button onClick={(e) => { e.stopPropagation(); console.log('button clicked'); }}>
    Click me
  </button>
</div>

Child tells parent: functions as props

A child component cannot change the state of its parent directly. Instead the parent passes a function as a prop, and the child calls it when something happens. This is how data flows up in React.

function LikeButton({ onLike }) {
  return <button onClick={onLike}>Like</button>;
}

function Post() {
  const [likes, setLikes] = useState(0);
  return (
    <div>
      <p>{likes} likes</p>
      <LikeButton onLike={() => setLikes(likes + 1)} />
    </div>
  );
}

A common name for such props is onSomething, like onLike, onSave or onDelete. It tells the reader that this prop is an event that the parent can listen to.

Good habits for events

  • Use real button elements for clicks, not a div. Buttons work with the keyboard and with screen readers for free. Add type="button" to buttons inside forms that should not submit.
  • Keep handlers short. If a handler grows, move the work into a normal function.
  • Do not forget preventDefault in form submit handlers.
  • Disable a button while a request is running, so people cannot click twice.

Common mistakes

  • Writing onclick (small letters). It must be onClick.
  • Calling the function: onClick={save()}.
  • Forgetting event.preventDefault() in a form, and then wondering why the page reloads.
  • Using a div with onClick for something that is really a button.
  • Reading event inside a delayed function after it ended. Copy what you need first: const value = event.target.value.

A worked example: a tiny color picker

Let us put several events in one small component. It has buttons that choose a colour, a text box for a label, and a form that saves the choice.

function ColorPicker() {
  const [color, setColor] = useState('tomato');
  const [label, setLabel] = useState('');
  const [saved, setSaved] = useState([]);

  function handleSubmit(event) {
    event.preventDefault();
    if (!label.trim()) return;
    setSaved([...saved, { id: crypto.randomUUID(), label: label.trim(), color }]);
    setLabel('');
  }

  return (
    <div>
      {['tomato', 'seagreen', 'royalblue'].map((c) => (
        <button key={c} type="button" onClick={() => setColor(c)} style={{ background: c, color: 'white' }}>
          {c}
        </button>
      ))}
      <form onSubmit={handleSubmit}>
        <input value={label} onChange={(e) => setLabel(e.target.value)} placeholder="Name this colour" />
        <button type="submit">Save</button>
      </form>
      <ul>
        {saved.map((s) => <li key={s.id} style={{ color: s.color }}>{s.label}</li>)}
      </ul>
    </div>
  );
}

There are four kinds of events in this one small component: onClick on the colour buttons (with an argument), onChange on the input, onSubmit on the form, and the state updates that follow. Each event calls a function, the function changes state, and React draws the new screen. That loop is how every React app works. Notice also that the colour buttons have type="button", so they do not submit the form by mistake.

Questions beginners often ask

Why do I use an arrow function in onClick sometimes and not other times? If the handler needs no argument, pass the function name: onClick={handleSave}. If you need to give it an argument, such as an id, wrap the call: onClick={() => handleRemove(id)}. The wrapper waits until the click before it calls your function.

What is the difference between onChange and onInput? In React you almost always use onChange. It fires on every key press in a text input, which is what you want for keeping state in step with the field.

Can I use the same handler for many buttons? Yes. Pass different arguments in the arrow function, or read information from the event, such as event.currentTarget.dataset.id. One handler for many items keeps your code short.

Why does my page reload when I press a button in a form? A button inside a form submits it by default. Add type="button" to buttons that should not submit, and use event.preventDefault() in the submit handler.

Your task

  1. Make a button that toggles a message between “On” and “Off”.
  2. Make a text box that shows how many characters have been typed.
  3. Make a small form with a name field. On submit, add the name to a list on the page, and clear the input.
  4. Add a child component DeleteButton that calls an onDelete prop, and use it to remove names.

How to check it worked

  • The page does not reload when you submit the form.
  • Pressing Enter in the input submits the form.
  • Deleting a name removes only that name.

Summary

  • Events use camelCase props such as onClick. Pass a function, do not call it.
  • Use an arrow function to pass arguments: () => remove(id).
  • The event object gives target.value, key and preventDefault().
  • Child components tell the parent about events through function props.

Frequently asked questions

Why does my onClick run when the page loads?

You wrote onClick={handleClick()}, which calls the function at once. Write onClick={handleClick} or onClick={() => handleClick()}.

What is event.preventDefault() for?

It stops the normal browser action. In a form it stops the page from reloading when the form is submitted.

What is the difference between onChange in React and in HTML?

In React, onChange fires on every key press in a text input, so you can keep state in step with what the person types.

How does a child component change the parent state?

The parent passes a function as a prop. The child calls it, and the parent changes its own state inside that function.