About meArticles

A better way to create event listeners in React

A better way to create event listeners in ReactA better way to create event listeners in React

In JavaScript, we can write code that listens and reacts to various events using DOM event listeners. This can be easily accomplished by using the addEventListener method, which is available on any HTML element. However, in React, calling addEventListener directly on an element can cause some performance issues.

In this short article, we will learn a better and more optimized way of creating event listeners in React.

TL;DR: Use useRef to reference the element and add the event listener to the ref instead of adding the event listener directly to the element.

Adding Eventlisteners using Ref in React.

Instead of adding event listeners directly to the document object or HTML element as we do in vanilla JavaScript, we can create a reference to it using React’s useRef hook (or createRef method, which is used in class-based components). Then, we can add the event listener to the ref (reference) instead of directly to the element.

Let's take a look at how this is done in the example below:

Here is an example of the JavaScript way of adding an event listener to the document object:

document.addEventListener("keydown", (e) => {

  // Code

});

However, in React, it is better to create a reference to the document object or HTML element using the useRef hook.

Here is an example of creating a reference to the document object:

import { useRef } from "react";


const documentRef = useRef(document); // document here is window.document


documentRef.current.addEventListener("keydown", () => {

  // Code

});

Alternatively, if we want to listen to an element and not the document object, we can create a reference to an HTML element and add the event listener to that reference:

import { useRef } from "react";


const App = () => {

  const divRef = useRef(); // Ref for HTML Element


  divRef.current.addEventListener("keydown", () => {

    // Code

  });

  

  return (

    <div ref={divRef}>

      <p>Hello World!</p>

    </div>

  );

}

Why is this important?

It’s important for performance. By using useRef or createRef and adding event listeners to the ref instead of directly to the document or HTML element, we avoid unnecessary re-renders and improve performance. This is because adding event listeners directly to the document or HTML element can cause React to re-render the component, even when the changes made by the event listener are not relevant to the component itself. By using a ref, we can ensure that the component only re-renders when its own state or props change, and not because of changes made by event listeners.

Other Performance tips.

  • If the event listener is in a useEffect (as would be the case 99% of the time), always unsubscribe from the event when the page/component gets unmounted. Adding event listeners without properly removing them can lead to memory leaks and degraded performance over time, especially in complex applications.

Example below:

import { useEffect, useRef } from 'react';


const documentRef = useRef(document);


const handleKeyDown = (e) => {

  if (e.key === "Escape") {

    // Run for your dear life

  }

};


useEffect(() => {

  documentRef.current.addEventListener("keydown", handleKeyDown);


  return () => {

    documentRef.current.removeEventListener("keydown", handleKeyDown);

  };

}, []);

  • Memoize your event handler functions with useCallback to also avoid unnecessary re-renders because when using useCallback  the function reference doesn’t change unnecessarily.  

    Let’s refactor the above example to use useCallback

import { useCallback, useEffect, useRef } from 'react';


const documentRef = useRef(document);


const handleKeyDown = useCallback((e) => {

    if (e.key === "Escape") {

      // Run for your dear life

    }

  }, []);


useEffect(() => {

  documentRef.current.addEventListener("keydown", handleKeyDown);


  return () => {

    documentRef.current.removeEventListener("keydown", handleKeyDown);

  };

}, []);

Conclusion

As software developers, one of our duties is to write efficient and highly performant code. In this article, we learned how to improve performance in React by using useRef or createRef when adding event listeners to elements or the document object. We also learned how to memoize event handler functions with useCallback, and unsubscribe from events when the component unmounts, to further optimize our code for better performance. These practices can help prevent unnecessary re-renders and improve the overall efficiency of our React applications.

It’s important to note that in this article, I used function-based components; hence, all the code snippets utilize the useRef hook. To use this approach in class-based components, you will have to create refs with createRef method.

Resources

Published Sep 6th, 2024

#react

#performance

#eventlistener

#javascript

#best-practices