DailyDevDiet

logo - dailydevdiet

Learn. Build. Innovate. Elevate your coding skills with dailydevdiet!

Chapter 9: Event Handling in React

Event Handling in React

Introduction

Event handling in React is a fundamental aspect of building interactive web applications. With React, handling events follows a similar pattern to handling events in DOM elements, but with some syntactic differences. Understanding how to properly handle events in React will allow you to create responsive and interactive user interfaces.

Event Handling Fundamentals

In React, events are named using camelCase rather than lowercase. Additionally, with JSX, you pass a function as the event handler, rather than a string.

HTML vs React Event Handling

HTML:

<button onclick="handleClick()">Click Me</button>

React:

<button onClick={handleClick}>Click Me</button>

Key differences:

  • React events use camelCase naming convention (e.g., onClick instead of onclick)
  • In React, you pass a function reference as the event handler, not a string
  • React implements a synthetic event system that wraps the native browser events

Common React Events

React supports a wide range of events, including:

Mouse Events

  • onClick
  • onDoubleClick
  • onMouseDown
  • onMouseUp
  • onMouseOver
  • onMouseOut
  • onMouseMove

Form Events

  • onChange
  • onSubmit
  • onFocus
  • onBlur

Keyboard Events

  • onKeyDown
  • onKeyPress
  • onKeyUp

Touch Events

  • onTouchStart
  • onTouchMove
  • onTouchEnd

Event Handlers in Class Components

In class components, event handlers are typically methods on the class:

class Button extends React.Component {
  constructor(props) {
    super(props);
    // Binding the event handler method to the class instance
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick(event) {
    console.log('Button clicked!');
    console.log(event); // React synthetic event
  }

  render() {
    return (
      <button onClick={this.handleClick}>
        Click Me
      </button>
    );
  }
}

Binding Event Handlers

When using class components, you need to bind this to the event handler methods to maintain the correct context. There are several approaches:

  1. Binding in the constructor (recommended):
constructor(props) {
  super(props);
  this.handleClick = this.handleClick.bind(this);
}
  1. Using public class fields syntax:
handleClick = (event) => {
  console.log('Button clicked!');
}
  1. Binding inline in the render method:
render() {
  return <button onClick={this.handleClick.bind(this)}>Click Me</button>;
}
  1. Using arrow functions in the render method:
render() {
  return <button onClick={(e) => this.handleClick(e)}>Click Me</button>;
}

The first two approaches are preferred for performance reasons, as the third and fourth approaches create a new function on each render.

Event Handlers in Functional Components

With functional components, event handlers are simpler to implement: Handlers

Sometimes you need to pass additional data to your event handlers. There are two common approaches:

Using Arrow Functions

function ItemList({ items }) {
  const handleItemClick = (id, event) => {
    console.log(`Item ${id} clicked`);
  };

  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={(e) => handleItemClick(item.id, e)}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

Using bind

function ItemList({ items }) {
  const handleItemClick = (id, event) => {
    console.log(`Item ${id} clicked`);
  };

  return (
    <ul>
      {items.map(item => (
        <li key={item.id} onClick={handleItemClick.bind(null, item.id)}>
          {item.name}
        </li>
      ))}
    </ul>
  );
}

Event Propagation in React

React’s event system handles event propagation similar to the DOM:

Event Bubbling

Events bubble up from the target element to the root, allowing you to handle events at a higher level:

function Parent() {
  const handleParentClick = () => {
    console.log('Parent clicked');
  };

  const handleChildClick = (e) => {
    console.log('Child clicked');
    // Prevent event from reaching parent
    e.stopPropagation();
  };

  return (
    <div onClick={handleParentClick}>
      Parent
      <button onClick={handleChildClick}>Child</button>
    </div>
  );
}

Event Capturing

React also supports event capturing phase with the onClickCapture style event props:

<div onClickCapture={() => console.log('Capture phase')}>
  <button onClick={() => console.log('Bubble phase')}>Click Me</button>
</div>

When the button is clicked, the events fire in this order:

  1. Capture phase (“Capture phase” is logged)
  2. Bubble phase (“Bubble phase” is logged)

Practical Examples

Toggle Component

import React, { useState } from 'react';

function Toggle() {
  const [isOn, setIsOn] = useState(false);

  const handleToggle = () => {
    setIsOn(prevState => !prevState);
  };

  return (
    <button onClick={handleToggle}>
      {isOn ? 'ON' : 'OFF'}
    </button>
  );
}

Form Input Handling

import React, { useState } from 'react';

function NameForm() {
  const [name, setName] = useState('');

  const handleChange = (event) => {
    setName(event.target.value);
  };

  const handleSubmit = (event) => {
    event.preventDefault();
    alert(`Submitted name: ${name}`);
  };

  return (
    <form onSubmit={handleSubmit}>
      <label>
        Name:
        <input type="text" value={name} onChange={handleChange} />
      </label>
      <button type="submit">Submit</button>
    </form>
  );
}

Multiple Events on the Same Element

function MultiEventButton() {
  const handleMouseEnter = () => console.log('Mouse entered');
  const handleMouseLeave = () => console.log('Mouse left');
  const handleClick = () => console.log('Button clicked');

  return (
    <button
      onClick={handleClick}
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
    >
      Hover and Click Me
    </button>
  );
}

Best Practices for Event Handling

  1. Avoid Inline Function Definitions: Define event handlers outside the JSX to prevent unnecessary re-renders.
// 🚫 Not recommended
<button onClick={() => handleClick(id)}>Click</button>

// ✅ Better approach
const handleButtonClick = useCallback(() => handleClick(id), [id]);<button onClick={handleButtonClick}>Click</button>
  1. Use Event Delegation: Instead of attaching event handlers to each element, attach a single handler to a parent element.
  2. Clean Up Event Listeners: When using addEventListener directly (rare in React), make sure to clean up with removeEventListener in the cleanup function of useEffect.
  3. Debounce or Throttle Expensive Events: For events like scrolling, resizing, or typing, consider using debounce or throttle functions.
import { debounce } from 'lodash';

function SearchInput() {
  const handleSearch = (e) => {
    // Expensive operation
  };

  const debouncedSearch = useCallback(
    debounce(handleSearch, 300),
    []
  );

  return <input onChange={debouncedSearch} />;
}
  1. Keep Event Handlers Simple: Logic should be separated from event handlers when possible.

Working with Custom Events

React components can also work with custom events:

Creating a Custom Event Component

import React, { useState, useEffect } from 'react';

function CustomEventComponent({ onCustomEvent }) {
  useEffect(() => {
    // Create a custom event handler
    const handleCustomEvent = (event) => {
      onCustomEvent(event.detail);
    };

    // Add event listener
    document.addEventListener('myCustomEvent', handleCustomEvent);

    // Clean up
    return () => {
      document.removeEventListener('myCustomEvent', handleCustomEvent);
    };
  }, [onCustomEvent]);

  const triggerCustomEvent = () => {
    // Dispatch a custom event
    const event = new CustomEvent('myCustomEvent', {
      detail: { message: 'Hello from custom event!' }
    });
    document.dispatchEvent(event);
  };

  return (
    <button onClick={triggerCustomEvent}>
      Trigger Custom Event
    </button>
  );
}

Common Gotchas and Solutions

Event Handlers Being Called During Render

// 🚫 This will call the function immediately during render
<button onClick={handleClick()}>Click Me</button>

// ✅ This passes the function reference, which is called when clicked
<button onClick={handleClick}>Click Me</button>

Handling Events Inside Map Functions

function List({ items }) {
  // Create a factory function to avoid recreating handlers in each render
  const createClickHandler = (id) => (event) => {
    console.log(`Item ${id} clicked`);
  };

  return (
    <ul>
      {items.map(item => {
        // Create a handler for this specific item
        const handleClick = createClickHandler(item.id);
        return (
          <li key={item.id} onClick={handleClick}>
            {item.name}
          </li>
        );
      })}
    </ul>
  );
}

Summary

Event handling is a core part of building interactive React applications. Key takeaways:

  • React uses camelCase for event names and passes functions as event handlers
  • React’s synthetic event system ensures cross-browser compatibility
  • Class components require binding to maintain the correct this context
  • Functional components make event handling simpler with hooks and closures
  • Proper event handling patterns can significantly impact application performance

In the next chapter, we’ll explore state management basics, which works hand-in-hand with event handling to create dynamic user interfaces.

Scroll to Top