React BasicsJanuary 10, 2025

Event Handling in React - Complete Guide

Learn how to handle events in React. Understand onClick, onChange, onSubmit and other common events with examples.

Author: Sushil Kumar

reacteventsonClickonChangeevent-handling

Event Handling in React - Complete Guide

Event handling is how React responds to user interactions. When someone clicks a button, types in an input, or submits a form, you need to handle those events. React's event system is similar to HTML events, but there are some important differences you should know about.

Basic Event Handling

onClick Event

The most common event is probably onClick. Here's how it works:

function Button() {
  const handleClick = () => {
    alert('Button clicked!');
  };
 
  return <button onClick={handleClick}>Click me</button>;
}

You can also use inline functions:

function Button() {
  return (
    <button onClick={() => alert('Clicked!')}>
      Click me
    </button>
  );
}

Both ways work! Use named functions for complex logic, and inline functions for simple stuff.

Common Events

onChange Event

This fires when an input's value changes:

function Input() {
  const [value, setValue] = useState('');
 
  const handleChange = (e) => {
    setValue(e.target.value);
  };
 
  return (
    <input
      type="text"
      value={value}
      onChange={handleChange}
      placeholder="Type something..."
    />
  );
}

This is how you make controlled inputs in React. The input's value is controlled by state!

onSubmit Event

Handle form submissions:

function Form() {
  const [name, setName] = useState('');
 
  const handleSubmit = (e) => {
    e.preventDefault();
    alert(`Submitted: ${name}`);
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={name}
        onChange={(e) => setName(e.target.value)}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Notice e.preventDefault() - this stops the form from refreshing the page, which is usually what you want in React apps.

onMouseOver and onMouseOut

These fire when the mouse enters or leaves an element:

function HoverButton() {
  const [isHovered, setIsHovered] = useState(false);
 
  return (
    <button
      onMouseOver={() => setIsHovered(true)}
      onMouseOut={() => setIsHovered(false)}
      style={{
        backgroundColor: isHovered ? 'blue' : 'gray'
      }}
    >
      Hover me
    </button>
  );
}

Great for hover effects and tooltips!

Event Object

Event handler functions automatically receive an event object:

function Input() {
  const handleChange = (e) => {
    console.log(e.target.value); // Input value
    console.log(e.target.name);  // Input name
    console.log(e.type);         // Event type
  };
 
  return <input onChange={handleChange} />;
}

The e.target gives you access to the element that triggered the event. Super useful!

Synthetic Events

React uses Synthetic Events, which wrap native browser events. This gives React more control and better performance:

function Button() {
  const handleClick = (e) => {
    e.preventDefault(); // Prevent default behavior
    e.stopPropagation(); // Stop event bubbling
    console.log('Clicked!');
  };
 
  return <button onClick={handleClick}>Click</button>;
}

Synthetic events work the same as native events, but React optimizes them under the hood.

Passing Arguments

Sometimes you need to pass extra data to your event handler:

function TodoList({ todos }) {
  const handleDelete = (id) => {
    console.log('Deleting:', id);
  };
 
  return (
    <ul>
      {todos.map(todo => (
        <li key={todo.id}>
          {todo.text}
          <button onClick={() => handleDelete(todo.id)}>
            Delete
          </button>
        </li>
      ))}
    </ul>
  );
}

The arrow function () => handleDelete(todo.id) lets you pass the todo.id to the handler. This is super common!

Event Handler Best Practices

1. Use Named Functions

For complex logic, use named functions:

// Good - Easy to read and test
function Component() {
  const handleClick = () => { 
    // Complex logic here
  };
  return <button onClick={handleClick}>Click</button>;
}
 
// Avoid for complex logic
function Component() {
  return <button onClick={() => { 
    // Too much logic inline
  }}>Click</button>;
}

Named functions are easier to read, test, and debug.

2. useCallback for Performance

If you're passing handlers to child components, use useCallback:

import { useCallback } from 'react';
 
function Component() {
  const handleClick = useCallback(() => {
    // Handler logic
  }, []);
 
  return <button onClick={handleClick}>Click</button>;
}

This prevents unnecessary re-renders of child components.

3. Prevent Default Behavior

Always prevent default when needed:

function Form() {
  const handleSubmit = (e) => {
    e.preventDefault(); // Prevent form submission
    // Your form handling logic
  };
 
  return <form onSubmit={handleSubmit}>...</form>;
}

Without preventDefault(), the form would submit normally and refresh the page.

Multiple Event Handlers

You can attach multiple handlers to one element:

function Input() {
  const [value, setValue] = useState('');
 
  const handleChange = (e) => {
    setValue(e.target.value);
  };
 
  const handleFocus = () => {
    console.log('Input focused');
  };
 
  const handleBlur = () => {
    console.log('Input blurred');
  };
 
  return (
    <input
      value={value}
      onChange={handleChange}
      onFocus={handleFocus}
      onBlur={handleBlur}
    />
  );
}

Each event type can have its own handler. Pretty flexible!

Event Bubbling

Events bubble up through the component tree in React:

function Parent() {
  const handleParentClick = () => {
    console.log('Parent clicked');
  };
 
  return (
    <div onClick={handleParentClick}>
      <Child />
    </div>
  );
}
 
function Child() {
  const handleChildClick = (e) => {
    e.stopPropagation(); // Stop bubbling
    console.log('Child clicked');
  };
 
  return <button onClick={handleChildClick}>Click</button>;
}

If you click the button, both handlers fire unless you call stopPropagation(). This is useful sometimes, annoying other times!

Common Event Types

Here's a quick reference of common events:

Event Description
onClick Mouse click
onChange Input value change
onSubmit Form submission
onFocus Element focused
onBlur Element blurred
onMouseOver Mouse enters element
onMouseOut Mouse leaves element
onKeyDown Key pressed
onKeyUp Key released
onDoubleClick Double click
onMouseEnter Mouse enters (doesn't bubble)
onMouseLeave Mouse leaves (doesn't bubble)

Real-World Example

Here's a complete example combining multiple events:

function SearchBox() {
  const [query, setQuery] = useState('');
  const [isFocused, setIsFocused] = useState(false);
 
  const handleChange = (e) => {
    setQuery(e.target.value);
  };
 
  const handleSubmit = (e) => {
    e.preventDefault();
    if (query.trim()) {
      console.log('Searching for:', query);
      // Perform search
    }
  };
 
  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      handleSubmit(e);
    }
  };
 
  return (
    <form onSubmit={handleSubmit}>
      <input
        type="text"
        value={query}
        onChange={handleChange}
        onFocus={() => setIsFocused(true)}
        onBlur={() => setIsFocused(false)}
        onKeyDown={handleKeyDown}
        placeholder="Search..."
        className={isFocused ? 'focused' : ''}
      />
      <button type="submit">Search</button>
    </form>
  );
}

This shows how multiple events work together in a real component.

Tips and Tricks

  1. Always use camelCase - onClick, not onclick
  2. Pass functions, not calls - onClick={handleClick}, not onClick={handleClick()}
  3. Use preventDefault when needed - Especially for forms
  4. Stop propagation when needed - To prevent unwanted bubbling
  5. Name handlers clearly - handleClick, handleSubmit, etc.

Common Mistakes

Here's what to avoid:

  1. Calling the function instead of passing it

    // Wrong
    <button onClick={handleClick()}>
     
    // Right
    <button onClick={handleClick}>
  2. Forgetting preventDefault on forms

    // Wrong - Page refreshes
    <form onSubmit={handleSubmit}>
     
    // Right
    <form onSubmit={(e) => { e.preventDefault(); handleSubmit(); }}>
  3. Not using camelCase

    // Wrong
    <button onclick={handleClick}>
     
    // Right
    <button onClick={handleClick}>

Conclusion

Event handling is essential in React. It's how your app responds to users. Once you understand the basics, you'll be building interactive apps in no time!

Remember:

  • Use camelCase for event names
  • Pass functions, don't call them
  • Use preventDefault for forms
  • Handle events to make your app interactive

Visual Explanation: Event Flow

Here's how events work in React:

User Action (Click Button)


┌──────────────┐
│ Browser      │
│ Detects      │
│ Event        │
└──────┬───────┘


┌──────────────┐
│ React        │
│ Handles      │
│ Event        │
└──────┬───────┘


┌──────────────┐
│ Your Handler │
│ Function     │
│ Executes     │
└──────┬───────┘


┌──────────────┐
│ State Updates│
│ UI Updates   │
└──────────────┘

Event Propagation Flow

┌─────────────────────────────┐
│ Parent Element              │
│ onClick (capture phase)     │
│   ┌─────────────────────┐  │
│   │ Child Element        │  │
│   │ onClick (target)     │  │
│   └─────────────────────┘  │
│ onClick (bubble phase)      │
└─────────────────────────────┘
 
Event flows: Capture → Target → Bubble

Frequently Asked Questions (FAQ)

Q1: What's the difference between onClick and addEventListener?

A:

  • onClick - React's way (recommended)
  • addEventListener - Vanilla JS way

Use React's onClick - it's cleaner and handles cleanup automatically.

Q2: Can I pass parameters to event handlers?

A: Yes! Use arrow functions:

<button onClick={() => handleClick(id)}>Click</button>

Or bind:

<button onClick={handleClick.bind(null, id)}>Click</button>

Q3: What is event.preventDefault()?

A: It prevents the default browser behavior:

const handleSubmit = (e) => {
  e.preventDefault(); // Prevents form submission
  // Your code
};

Q4: What is event.stopPropagation()?

A: It stops the event from bubbling up to parent elements:

const handleClick = (e) => {
  e.stopPropagation(); // Stops parent from receiving event
};

Q5: Can I use multiple event handlers?

A: Yes! You can have multiple handlers, but React's onClick only takes one function. Chain them if needed:

<button onClick={(e) => {
  handleClick1(e);
  handleClick2(e);
}}>Click</button>

Q6: How do I get the event object?

A: It's automatically passed as the first parameter:

const handleClick = (e) => {
  console.log(e); // Event object
  console.log(e.target); // Element that was clicked
};

Q7: Can I use events on custom components?

A: Yes, but you need to pass them through:

function CustomButton({ onClick }) {
  return <button onClick={onClick}>Click</button>;
}

Q8: What's the difference between onChange and onInput?

A:

  • onChange - Fires when value changes AND element loses focus
  • onInput - Fires on every keystroke (not commonly used in React)

Use onChange for most cases.

Q9: How do I handle keyboard events?

A: Use onKeyDown, onKeyUp, or onKeyPress:

<input onKeyDown={(e) => {
  if (e.key === 'Enter') {
    handleSubmit();
  }
}} />

Q10: Can I prevent default behavior conditionally?

A: Yes! Check condition first:

const handleSubmit = (e) => {
  if (!isValid) {
    e.preventDefault();
  }
};

Next Steps

Now that you understand events, here's what to learn next:

Happy coding! 🎉