How to Use AbortController for Cleaner Event Listener Cleanup in React

Normally, React developers clean up event listeners by manually calling removeEventListener in a useEffect cleanup. But there’s a cleaner, more modern way: using AbortController to automatically handle cleanup without explicit unbinding. Traditional Event Listener Setup Here’s the classic pattern you’re probably using already: useEffect(() => { const handleEsc = (e) => { if (e.key === "Escape") { onClose(); } }; window.addEventListener("keydown", handleEsc); return () => { window.removeEventListener("keydown", handleEsc); }; }, []); Cleaner Alternative Using AbortController Here’s how to refactor it to use an AbortController instead: useEffect(() => { const controller = new AbortController(); const { signal } = controller; const handleEsc = (e) => { if (e.key === "Escape") { onClose(); } }; window.addEventListener("keydown", handleEsc, { signal }); return () => controller.abort(); }, []); How It Works When you pass the signal to addEventListener, the browser automatically removes the listener when controller.abort() is called during the effect's cleanup. This reduces boilerplate and risk of bugs from missing cleanups. Pros and Cons ✅ Pros Automatic, cleaner cleanup without manual unbinding Reduced chance of memory leaks Better readability and modern browser support ⚠️ Cons Requires relatively modern browsers (Chrome 88+, Firefox 85+) Can seem unfamiliar to some teams at first

Apr 27, 2025 - 17:32
 0
How to Use AbortController for Cleaner Event Listener Cleanup in React

Normally, React developers clean up event listeners by manually calling removeEventListener in a useEffect cleanup. But there’s a cleaner, more modern way: using AbortController to automatically handle cleanup without explicit unbinding.

Traditional Event Listener Setup

Here’s the classic pattern you’re probably using already:

useEffect(() => {
  const handleEsc = (e) => {
    if (e.key === "Escape") {
      onClose();
    }
  };

  window.addEventListener("keydown", handleEsc);

  return () => {
    window.removeEventListener("keydown", handleEsc);
  };
}, []);

Cleaner Alternative Using AbortController

Here’s how to refactor it to use an AbortController instead:

useEffect(() => {
  const controller = new AbortController();
  const { signal } = controller;

  const handleEsc = (e) => {
    if (e.key === "Escape") {
      onClose();
    }
  };

  window.addEventListener("keydown", handleEsc, { signal });

  return () => controller.abort();
}, []);

How It Works

When you pass the signal to addEventListener, the browser automatically removes the listener when controller.abort() is called during the effect's cleanup. This reduces boilerplate and risk of bugs from missing cleanups.

Pros and Cons

✅ Pros

  • Automatic, cleaner cleanup without manual unbinding
  • Reduced chance of memory leaks
  • Better readability and modern browser support

⚠️ Cons

  • Requires relatively modern browsers (Chrome 88+, Firefox 85+)
  • Can seem unfamiliar to some teams at first