---
title: "Event Listener vs useState: A Performance Perspective"
description: "How to optimize React apps by using event listeners in useEffect instead of frequent state updates. Improve performance and avoid unnecessary re-renders!"
slug: "event-listener-vs-usestate-a-performance-perspective"
url: "https://furkanbaytekin.dev/blogs/event-listener-vs-usestate-a-performance-perspective"
author: "Furkan Baytekin"
category: "Software"
tags: []
published: 2024-12-17T08:02:45Z
updated: 2024-12-17T08:02:45Z
readTime: 4
cover: "https://myblogcms.furkanbaytekin.dev/uploads/cover/using-event-listeners-in-useeffect-over-updating-state-a-performance-perspective+1734370751900"
---

React’s `useEffect` hook is a powerful tool for handling side effects, such as interacting with external APIs, subscriptions, or DOM manipulations. However, how you use `useEffect` can significantly impact the performance of your application. A common performance pitfall occurs when developers overuse state updates for tasks that could instead leverage event listeners.

In this post, we’ll explore why using event listeners directly can sometimes be a more efficient approach compared to relying on React state, especially for frequent updates. We’ll walk through two approaches and analyze their impact on performance.

---

## Why Updating State in `useEffect` Can Be Costly

When `useEffect` updates state, it triggers a component re-render. While re-renders are fundamental to React’s design, frequent updates can lead to:

1. **Excessive Re-renders:** Unnecessary re-renders propagate to child components, increasing rendering costs.
2. **Performance Bottlenecks:** Components reprocess logic when state updates aren’t critical for UI changes.
3. **UI Lag:** Frequent updates, like tracking mouse movement, can degrade the responsiveness of your application.

---

## Event Listeners to the Rescue

Event listeners allow you to interact directly with the DOM without relying on React state updates. This can significantly improve performance for tasks like mouse tracking or scroll events, where frequent updates are needed.

---

### Example: Tracking Mouse Movement
We’ll compare two approaches for moving an element horizontally based on the mouse’s X position:

1. **Using `useState` to update the position.**
2. **Using an event listener and `useRef` for direct DOM manipulation.**

---

### Approach 1: Updating State in `useEffect`

```tsx
"use client"

import { FC, useEffect, useState } from "react"

const MouseTrackerWithState: FC = () => {
  const [mouseX, setMouseX] = useState(0)

  useEffect(() => {
    const handleMouseMove = (event: MouseEvent) => {
      setMouseX(event.clientX)
    }

    window.addEventListener("mousemove", handleMouseMove)

    return () => {
      window.removeEventListener("mousemove", handleMouseMove)
    }
  }, [])

  return (
    <div style={{ marginLeft: `${mouseX}px` }}>
      Hello World
    </div>
  )
}

export default MouseTrackerWithState
```

**Issues with This Approach:**
- **Re-rendering:** Every `mousemove` event triggers a `setState`, causing the component to re-render.
- **Performance Overhead:** In scenarios with frequent updates, such as mouse tracking, state updates become costly, leading to laggy UI performance.

---

### Approach 2: Using Event Listener and `useRef`

```tsx
"use client"

import { FC, useEffect, useRef } from "react"

const MouseTrackerWithListener: FC = () => {
  const selfRef = useRef<HTMLDivElement | null>(null)

  useEffect(() => {
    const handleMouseMove = (event: MouseEvent) => {
      if (!selfRef.current) return
      selfRef.current.style.marginLeft = `${event.clientX}px`
    }

    window.addEventListener("mousemove", handleMouseMove)

    return () => {
      window.removeEventListener("mousemove", handleMouseMove)
    }
  }, [])

  return (
    <div ref={selfRef}>
      Hello World
    </div>
  )
}

export default MouseTrackerWithListener
```

**Benefits of This Approach:**
- **No Re-renders:** By directly manipulating the DOM element’s `style`, you avoid triggering React re-renders altogether.
- **Improved Performance:** This method is far more efficient for frequent updates like mouse movements because React state isn’t involved.

---

## Performance Comparison

| Metric                       | `useState` Approach           | Event Listener with `useRef`     |
|------------------------------|--------------------------------|----------------------------------|
| **Re-renders**               | Frequent on every update      | None                             |
| **React State Overhead**     | Yes                           | No                               |
| **Performance Efficiency**   | Laggy under frequent updates  | Smooth and responsive            |

---

## When to Use Event Listeners Over State

Event listeners are ideal when:

1. **Frequent Updates Are Needed:** For tasks like mouse tracking, scroll monitoring, or resizing.
2. **State Isn’t Required for UI Rendering:** If the value only modifies the DOM without affecting React’s state logic.
3. **Performance Is Critical:** For high-frequency updates, avoiding unnecessary re-renders ensures a smooth user experience.

---

## Best Practices for Event Listeners in `useEffect`

1. **Use `useRef`** for mutable values that don’t require re-renders.
2. **Always Cleanup:** Remove event listeners in the `useEffect` cleanup function to prevent memory leaks.
3. **Throttle or Debounce:** For extremely high-frequency updates, consider throttling or debouncing the event listener logic.

---

## Conclusion

While React’s state management is a powerful tool, it’s not always the most efficient choice for tasks requiring frequent updates. By directly using event listeners with `useRef`, you can avoid unnecessary re-renders and significantly improve performance.

For scenarios like mouse movement, scroll tracking, or window resizing, consider bypassing React state and manipulating the DOM directly. This approach ensures smoother interactions and a more responsive user experience.

Choose the right tool for the job, and optimize your React applications for performance wherever it matters most.
