How to handle events in React?

👨‍💻 Frontend Developer 🟠 May come up 🎚️ Medium
#React

Brief Answer

Event handling in React works through SyntheticEvent — a wrapper over native events:

  1. onClick — handling clicks 🖱️
  2. onChange — value changes 📝
  3. onSubmit — form submissions 📤
  4. SyntheticEvent — cross-browser compatibility 🌐
  5. preventDefault() — cancel default behavior ⛔
  6. Delegation — events attached to root 🎯
function Button() {
  const handleClick = (e) => {
    e.preventDefault();
    console.log('Clicked!');
  };
 
  return <button onClick={handleClick}>Click me</button>;
}

Full Answer

Event handling in React is a powerful system that provides cross-browser compatibility and performance optimization! React uses SyntheticEvent to unify events. 🎭

1. Event handling basics

Functional components

function EventExample() {
  const handleClick = (event) => {
    console.log('Button clicked!', event);
  };
 
  const handleChange = (event) => {
    console.log('Input changed:', event.target.value);
  };
 
  return (
    <div>
      <button onClick={handleClick}>Click me</button>
      <input onChange={handleChange} placeholder="Type here" />
    </div>
  );
}

Class components

class EventExample extends React.Component {
  handleClick = (event) => {
    console.log('Button clicked!', event);
  };
 
  handleChange = (event) => {
    console.log('Input changed:', event.target.value);
  };
 
  render() {
    return (
      <div>
        <button onClick={this.handleClick}>Click me</button>
        <input onChange={this.handleChange} placeholder="Type here" />
      </div>
    );
  }
}

2. SyntheticEvent — event wrapper

React creates SyntheticEvent for cross-browser compatibility:

function SyntheticEventExample() {
  const handleEvent = (syntheticEvent) => {
    console.log('SyntheticEvent:', syntheticEvent);
    console.log('Native event:', syntheticEvent.nativeEvent);
    console.log('Target:', syntheticEvent.target);
    console.log('Type:', syntheticEvent.type);
    
    // Prevent default behavior
    syntheticEvent.preventDefault();
    syntheticEvent.stopPropagation();
  };
 
  return (
    <form onSubmit={handleEvent}>
      <input type="text" />
      <button type="submit">Submit</button>
    </form>
  );
}

Clicks and interaction

function ClickEvents() {
  return (
    <div>
      <button onClick={(e) => console.log('Click')}>Click</button>
      <button onDoubleClick={(e) => console.log('Double click')}>Double</button>
      <div onMouseEnter={(e) => console.log('Mouse enter')}>Hover me</div>
      <div onMouseLeave={(e) => console.log('Mouse leave')}>Leave me</div>
    </div>
  );
}

Forms and input

function FormEvents() {
  const [value, setValue] = useState('');
 
  return (
    <form onSubmit={(e) => {
      e.preventDefault();
      console.log('Form submitted:', value);
    }}>
      <input
        value={value}
        onChange={(e) => setValue(e.target.value)}
        onFocus={(e) => console.log('Input focused')}
        onBlur={(e) => console.log('Input blurred')}
      />
      <button type="submit">Submit</button>
    </form>
  );
}

Keyboard

function KeyboardEvents() {
  const handleKeyDown = (e) => {
    if (e.key === 'Enter') {
      console.log('Enter pressed');
    }
    if (e.key === 'Escape') {
      console.log('Escape pressed');
    }
  };
 
  return (
    <input
      onKeyDown={handleKeyDown}
      onKeyUp={(e) => console.log('Key up:', e.key)}
      placeholder="Press keys"
    />
  );
}

4. Passing parameters to handlers

Through arrow functions

function ParameterExample() {
  const handleClick = (id, name) => {
    console.log(`Clicked item ${id}: ${name}`);
  };
 
  const items = [
    { id: 1, name: 'Item 1' },
    { id: 2, name: 'Item 2' }
  ];
 
  return (
    <div>
      {items.map(item => (
        <button
          key={item.id}
          onClick={() => handleClick(item.id, item.name)}
        >
          {item.name}
        </button>
      ))}
    </div>
  );
}

Through data attributes

function DataAttributeExample() {
  const handleClick = (e) => {
    const id = e.target.dataset.id;
    const name = e.target.dataset.name;
    console.log(`Clicked item ${id}: ${name}`);
  };
 
  return (
    <div>
      <button data-id="1" data-name="Item 1" onClick={handleClick}>
        Item 1
      </button>
      <button data-id="2" data-name="Item 2" onClick={handleClick}>
        Item 2
      </button>
    </div>
  );
}

5. Practical examples

function Modal({ isOpen, onClose, children }) {
  const handleBackdropClick = (e) => {
    if (e.target === e.currentTarget) {
      onClose();
    }
  };
 
  const handleKeyDown = (e) => {
    if (e.key === 'Escape') {
      onClose();
    }
  };
 
  useEffect(() => {
    if (isOpen) {
      document.addEventListener('keydown', handleKeyDown);
      return () => document.removeEventListener('keydown', handleKeyDown);
    }
  }, [isOpen]);
 
  if (!isOpen) return null;
 
  return (
    <div className="modal-backdrop" onClick={handleBackdropClick}>
      <div className="modal-content">
        {children}
        <button onClick={onClose}>Close</button>
      </div>
    </div>
  );
}

Drag and Drop

function DragDropExample() {
  const [draggedItem, setDraggedItem] = useState(null);
 
  const handleDragStart = (e) => {
    setDraggedItem(e.target.textContent);
  };
 
  const handleDragOver = (e) => {
    e.preventDefault(); // Allow drop
  };
 
  const handleDrop = (e) => {
    e.preventDefault();
    console.log('Dropped:', draggedItem);
    setDraggedItem(null);
  };
 
  return (
    <div>
      <div
        draggable
        onDragStart={handleDragStart}
        style={{ padding: '10px', border: '1px solid #ccc' }}
      >
        Drag me
      </div>
      <div
        onDragOver={handleDragOver}
        onDrop={handleDrop}
        style={{ padding: '20px', border: '2px dashed #999' }}
      >
        Drop zone
      </div>
    </div>
  );
}

6. Performance optimization

useCallback for stable references

function OptimizedList({ items }) {
  const [selectedId, setSelectedId] = useState(null);
 
  // Memoize handler
  const handleSelect = useCallback((id) => {
    setSelectedId(id);
  }, []);
 
  return (
    <div>
      {items.map(item => (
        <OptimizedItem
          key={item.id}
          item={item}
          onSelect={handleSelect}
          isSelected={selectedId === item.id}
        />
      ))}
    </div>
  );
}
 
const OptimizedItem = React.memo(({ item, onSelect, isSelected }) => {
  return (
    <div
      onClick={() => onSelect(item.id)}
      style={{ background: isSelected ? '#e0e0e0' : 'white' }}
    >
      {item.name}
    </div>
  );
});

7. Event delegation

React automatically uses event delegation:

// React attaches events to root element
function EventDelegation() {
  const handleClick = (e) => {
    // e.target - element that was clicked
    // e.currentTarget - element with handler
    console.log('Clicked on:', e.target.tagName);
  };
 
  return (
    <div onClick={handleClick}>
      <p>Click on paragraph</p>
      <button>Click on button</button>
      <span>Click on span</span>
    </div>
  );
}

Best practices

  1. Use useCallback for optimization 🚀
  2. Avoid inline functions in render 📝
  3. Check event.target when delegating ✅
  4. Use preventDefault() when needed ⛔
  5. Clean up listeners in useEffect 🧹

Common mistakes

Wrong:

// Creates new function on every render
<button onClick={() => console.log('Click')}>Click</button>

Correct:

const handleClick = useCallback(() => {
  console.log('Click');
}, []);
 
<button onClick={handleClick}>Click</button>

Conclusion

Event handling in React:

  • SyntheticEvent — cross-browser compatibility
  • Delegation — automatic optimization
  • preventDefault/stopPropagation — behavior control
  • useCallback — performance optimization

Use events effectively to create interactive applications! 🎯