Frontend / React / synthetic_events.md

Synthetic Events in React

Updated 4 interview angles 6 min read source
On this page8
  1. Table of Contents
  2. What are Synthetic Events?
  3. Event Handling
  4. Event Pooling
  5. Best Practices
  6. Common Interview Questions
  7. Summary
  8. Interview angle

Synthetic Events in React

Synthetic events are React’s cross-browser wrapper around the browser’s native events. They provide a consistent API regardless of the browser being used.

Table of Contents

What are Synthetic Events?

Synthetic events are React’s wrapper around native DOM events. They provide a consistent interface across different browsers and normalize event behavior.

Key Characteristics:

  • Cross-browser: Work consistently across all browsers
  • Normalized: Same API regardless of browser differences
  • Pooled: Events are reused for performance
  • Synthetic: Not actual DOM events, but React’s abstraction

Event Object Structure:

javascript
// Synthetic event object
const syntheticEvent = {
  type: 'click',
  target: DOMElement,
  currentTarget: DOMElement,
  preventDefault: function() { /* ... */ },
  stopPropagation: function() { /* ... */ },
  nativeEvent: DOMEvent, // Access to original DOM event
  // ... other normalized properties
};

Event Handling

Basic Event Handling:

javascript
function Button() {
  const handleClick = (event) => {
    console.log('Button clicked!');
    console.log('Event type:', event.type);
    console.log('Target:', event.target);
    console.log('Native event:', event.nativeEvent);
  };

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

Event Prevention:

javascript
function Form() {
  const handleSubmit = (event) => {
    event.preventDefault(); // Prevent form submission
    event.stopPropagation(); // Stop event bubbling

    console.log('Form submitted');
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="text" />
      <button type="submit">Submit</button>
    </form>
  );
}

Event Parameters:

javascript
function InputField() {
  const handleChange = (event) => {
    const value = event.target.value;
    console.log('Input value:', value);
  };

  const handleKeyPress = (event) => {
    if (event.key === 'Enter') {
      console.log('Enter pressed');
    }
  };

  return (
    <input
      onChange={handleChange}
      onKeyPress={handleKeyPress}
      placeholder="Type something..."
    />
  );
}

Multiple Event Handlers:

javascript
function InteractiveElement() {
  const handleMouseEnter = (event) => {
    console.log('Mouse entered');
  };

  const handleMouseLeave = (event) => {
    console.log('Mouse left');
  };

  const handleClick = (event) => {
    console.log('Clicked');
  };

  return (
    <div
      onMouseEnter={handleMouseEnter}
      onMouseLeave={handleMouseLeave}
      onClick={handleClick}
      style={{ padding: '20px', border: '1px solid black' }}
    >
      Hover and click me
    </div>
  );
}

Event Pooling

What is Event Pooling:

React reuses event objects for performance. After an event handler finishes, the event object is returned to the pool and its properties are nullified.

Event Pooling Example:

javascript
function EventPoolingExample() {
  const handleClick = (event) => {
    // Good: Access event properties immediately
    console.log('Event type:', event.type);
    console.log('Target:', event.target);

    // Bad: Accessing event properties asynchronously
    setTimeout(() => {
      console.log('Event type:', event.type); // null
      console.log('Target:', event.target); // null
    }, 100);
  };

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

Handling Event Pooling:

javascript
function EventPoolingSolution() {
  const handleClick = (event) => {
    // Good: Store values immediately
    const eventType = event.type;
    const target = event.target;

    setTimeout(() => {
      console.log('Event type:', eventType); // Works
      console.log('Target:', target); // Works
    }, 100);
  };

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

Using event.persist():

javascript
function PersistExample() {
  const handleClick = (event) => {
    // Good: Persist the event for async use
    event.persist();

    setTimeout(() => {
      console.log('Event type:', event.type); // Works
      console.log('Target:', event.target); // Works
    }, 100);
  };

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

Best Practices

1. Access Event Properties Immediately:

javascript
// Good: Access properties immediately
function GoodHandler(event) {
  const value = event.target.value;
  const type = event.type;

  // Use values immediately or store them
  console.log(value, type);
}

// Bad: Access properties asynchronously
function BadHandler(event) {
  setTimeout(() => {
    console.log(event.target.value); // null
  }, 100);
}

2. Use event.persist() for Async Operations:

javascript
// Good: Persist event for async use
function AsyncHandler(event) {
  event.persist();

  fetch('/api/data')
    .then(() => {
      console.log('Event type:', event.type); // Works
    });
}

// Good: Store values immediately
function BetterAsyncHandler(event) {
  const eventData = {
    type: event.type,
    target: event.target,
    value: event.target.value
  };

  fetch('/api/data', {
    method: 'POST',
    body: JSON.stringify(eventData)
  });
}

3. Prevent Default Behavior:

javascript
// Good: Prevent default when needed
function FormHandler(event) {
  event.preventDefault();

  // Custom form handling
  const formData = new FormData(event.target);
  console.log('Form data:', formData);
}

// Good: Stop propagation when needed
function NestedHandler(event) {
  event.stopPropagation();

  // Handle event without bubbling to parent
  console.log('Handled locally');
}

4. Use Proper Event Types:

javascript
// Good: Use appropriate event handlers
function InputComponent() {
  const handleChange = (event) => {
    console.log('Value changed:', event.target.value);
  };

  const handleKeyDown = (event) => {
    if (event.key === 'Enter') {
      console.log('Enter pressed');
    }
  };

  const handleFocus = (event) => {
    console.log('Input focused');
  };

  return (
    <input
      onChange={handleChange}
      onKeyDown={handleKeyDown}
      onFocus={handleFocus}
    />
  );
}

5. Handle Multiple Events Efficiently:

javascript
// Good: Single handler for multiple events
function EfficientHandler() {
  const handleInteraction = (event) => {
    switch (event.type) {
      case 'click':
        console.log('Clicked');
        break;
      case 'mouseenter':
        console.log('Mouse entered');
        break;
      case 'mouseleave':
        console.log('Mouse left');
        break;
      default:
        break;
    }
  };

  return (
    <div
      onClick={handleInteraction}
      onMouseEnter={handleInteraction}
      onMouseLeave={handleInteraction}
    >
      Interactive element
    </div>
  );
}

Common Interview Questions

Q: What are synthetic events in React?

  • React’s cross-browser wrapper around native DOM events that provides a consistent API.

Q: Why does React use synthetic events?

  • To ensure consistent behavior across different browsers and normalize event handling.

Q: What is event pooling in React?

  • React reuses event objects for performance, nullifying their properties after the event handler finishes.

Q: How do you handle events in async operations?

  • Use event.persist() or store event properties immediately before async operations.

Q: What’s the difference between event.target and event.currentTarget?

  • event.target is the element that triggered the event, while event.currentTarget is the element that the event handler is attached to.

Q: How do you prevent default behavior in React?

  • Use event.preventDefault() to prevent the default browser behavior.

Q: How do you stop event propagation?

  • Use event.stopPropagation() to prevent the event from bubbling up to parent elements.

Q: Can you access the native DOM event?

  • Yes, through event.nativeEvent property.

Summary

  • Synthetic events are React’s cross-browser wrapper around native DOM events
  • Event pooling reuses event objects for performance optimization
  • Access event properties immediately or use event.persist() for async operations
  • preventDefault() and stopPropagation() control event behavior
  • event.target vs event.currentTarget have different meanings
  • event.nativeEvent provides access to the original DOM event
  • Synthetic events ensure consistent behavior across all browsers
  • Proper event handling is crucial for React performance and functionality

Interview angle 4

  • “What is a synthetic event?” - React’s cross-browser wrapper over the native event, with a consistent API. Since React 17 listeners are attached at the root container rather than document, which is what makes multiple React roots and React-inside-non-React apps behave.
  • “Do you still need e.persist()?” - no. Event pooling was removed in React 17, so the event object survives into async callbacks. Advice telling you to call persist() predates that.
  • “How does bubbling work through portals?” - along the React tree, not the DOM tree. A click inside a portalled modal reaches handlers on its React ancestors even though the DOM node is elsewhere.
  • “When do you reach for a native listener?” - for events React does not synthesise or that need non-passive preventDefault on scroll and touch, and for listeners on window or document. Add them in an effect and remove them in the cleanup.