React.js Fundamentals

Handling Events in React

15 min Lesson 7 of 40

Understanding Event Handling in React

Event handling in React is similar to handling events in vanilla JavaScript, but with some important syntactic and behavioral differences. React events are named using camelCase and you pass a function as the event handler rather than a string.

Key Difference: In HTML, you write onclick="handleClick()", but in React JSX, you write onClick={handleClick} without the parentheses (unless you're creating an inline arrow function).

Basic Event Handling

React wraps native browser events with synthetic events for cross-browser compatibility. Here's how to handle basic events:

function Button() { const handleClick = () => { console.log('Button clicked!'); }; const handleMouseEnter = () => { console.log('Mouse entered the button'); }; return ( <button onClick={handleClick} onMouseEnter={handleMouseEnter} > Click Me </button> ); }

Synthetic Events

React's synthetic event system wraps the native browser events. Synthetic events have the same interface as native events, including methods like stopPropagation() and preventDefault().

function Form() { const handleSubmit = (event) => { event.preventDefault(); // Prevent default form submission console.log('Form submitted'); }; const handleInput = (event) => { // event.target gives you the DOM element console.log('Input value:', event.target.value); }; return ( <form onSubmit={handleSubmit}> <input type="text" onChange={handleInput} placeholder="Enter text" /> <button type="submit">Submit</button> </form> ); }
Performance Tip: Define event handlers outside the render method when possible. Creating functions inside render (like inline arrow functions) creates a new function on every render, which can impact performance in large applications.

Passing Arguments to Event Handlers

When you need to pass additional parameters to event handlers, you have several options:

function ItemList() { const items = [ { id: 1, name: 'Apple' }, { id: 2, name: 'Banana' }, { id: 3, name: 'Cherry' } ]; // Method 1: Arrow function in JSX (creates new function each render) const handleDelete1 = (id, event) => { console.log('Delete item:', id); }; // Method 2: Using bind (creates new function, but can be optimized) const handleDelete2 = function(id, event) { console.log('Delete item:', id); }; // Method 3: Currying (recommended for complex scenarios) const handleDelete3 = (id) => (event) => { console.log('Delete item:', id); }; return ( <ul> {items.map(item => ( <li key={item.id}> {item.name} {/* Method 1: Inline arrow function */} <button onClick={(e) => handleDelete1(item.id, e)}> Delete 1 </button> {/* Method 2: Using bind */} <button onClick={handleDelete2.bind(null, item.id)}> Delete 2 </button> {/* Method 3: Curried function */} <button onClick={handleDelete3(item.id)}> Delete 3 </button> </li> ))} </ul> ); }
Common Mistake: Don't call the function immediately by adding parentheses: onClick={handleClick()}. This executes the function during render instead of when the event occurs. Use onClick={handleClick} instead.

Event Handler Naming Conventions

Follow these naming conventions for better code readability:

function Example() { // Handler functions typically start with "handle" const handleClick = () => { /* ... */ }; const handleSubmit = () => { /* ... */ }; const handleChange = () => { /* ... */ }; // For props passed to child components, use "on" prefix return ( <div> <button onClick={handleClick}>Click</button> <CustomButton onButtonClick={handleClick} /> </div> ); } function CustomButton({ onButtonClick }) { // Child component receives "on..." prop return <button onClick={onButtonClick}>Custom Button</button>; }

Common Event Types

React supports all standard DOM events. Here are the most commonly used ones:

function EventExamples() { const handleClick = () => console.log('Click event'); const handleDoubleClick = () => console.log('Double click event'); const handleMouseOver = () => console.log('Mouse over event'); const handleMouseOut = () => console.log('Mouse out event'); const handleKeyDown = (e) => console.log('Key pressed:', e.key); const handleFocus = () => console.log('Input focused'); const handleBlur = () => console.log('Input blurred'); const handleChange = (e) => console.log('Value changed:', e.target.value); return ( <div> {/* Mouse Events */} <button onClick={handleClick} onDoubleClick={handleDoubleClick} onMouseOver={handleMouseOver} onMouseOut={handleMouseOut} > Hover and Click </button> {/* Keyboard Events */} <input type="text" onKeyDown={handleKeyDown} onFocus={handleFocus} onBlur={handleBlur} onChange={handleChange} placeholder="Type here" /> </div> ); }

Event Bubbling and Stopping Propagation

Events in React bubble up the component tree just like in the DOM. You can stop this propagation when needed:

function EventBubbling() { const handleParentClick = () => { console.log('Parent clicked'); }; const handleChildClick = (event) => { event.stopPropagation(); // Prevents parent handler from firing console.log('Child clicked'); }; const handleLinkClick = (event) => { event.preventDefault(); // Prevents default browser behavior console.log('Link clicked, but navigation prevented'); }; return ( <div onClick={handleParentClick} style={{ padding: '20px', background: 'lightblue' }}> <p>Parent Container</p> <button onClick={handleChildClick}> Child Button (Click won't bubble to parent) </button> <br /> <a href="https://example.com" onClick={handleLinkClick}> Link (Navigation prevented) </a> </div> ); }
Best Practice: Use event.preventDefault() to prevent default browser behavior (like form submission or link navigation), and event.stopPropagation() to prevent event bubbling to parent elements.

Working with Input Events

Input events are crucial for building interactive forms. Here's a comprehensive example:

import { useState } from 'react'; function SearchBar() { const [query, setQuery] = useState(''); const [isFocused, setIsFocused] = useState(false); const handleChange = (event) => { setQuery(event.target.value); }; const handleKeyPress = (event) => { if (event.key === 'Enter') { console.log('Searching for:', query); } }; const handleClear = () => { setQuery(''); }; const handleFocus = () => setIsFocused(true); const handleBlur = () => setIsFocused(false); return ( <div style={{ border: isFocused ? '2px solid blue' : '1px solid gray' }}> <input type="text" value={query} onChange={handleChange} onKeyPress={handleKeyPress} onFocus={handleFocus} onBlur={handleBlur} placeholder="Search..." /> {query && ( <button onClick={handleClear}>Clear</button> )} <p>Searching: {query}</p> </div> ); }
Controlled Components: When you combine state with input events (like onChange), you create controlled components where React manages the input value through state. This gives you full control over the input behavior.

Exercise 1: Color Changer

Create a component that changes its background color based on user interaction:

  • Display a colored box (div with background color)
  • On click: change to a random color
  • On double-click: reset to white
  • On mouse enter: add a border
  • On mouse leave: remove the border
  • Display the current color name below the box

Hint: Use state to track the current color and border status.

Exercise 2: Task Manager with Keyboard Shortcuts

Build a simple task manager with keyboard support:

  • Input field to add new tasks
  • Press Enter to add a task
  • Press Escape to clear the input field
  • Display list of tasks with delete buttons
  • Click task text to mark as complete (strikethrough)
  • Show number of pending and completed tasks

Hint: Use onKeyDown to detect Enter and Escape keys (event.key === 'Enter').

Exercise 3: Interactive Image Gallery

Create an image gallery with mouse and click events:

  • Display 6 thumbnail images in a grid
  • On mouse over: show image title in a tooltip
  • On click: display the image in a larger view
  • Add "Previous" and "Next" buttons to navigate
  • On double-click: toggle fullscreen mode
  • Close button to exit fullscreen

Bonus: Add keyboard navigation with arrow keys.