Log duplication with asynchronous event handlers

13 Views Asked by At

I have a simple React snippet which produces surprising console output in the browser:

import { useEffect, useState, useCallback } from "react";

export default function App() {
  const [data, setData] = useState([]);
  const [isFetching, setIsFetching] = useState(false);

  const fetchData = useCallback(async () => {
    if (isFetching) return;
    setIsFetching(true);
    console.log("fetch 1");
    console.log("fetch 2");

    try {
      const response = await new Promise((resolve) => {
        setTimeout(() => {
          resolve(Math.floor(Math.random() * 10));
        }, 10);
      });
      setData((prev) => [...prev, response]);
    } finally {
      setIsFetching(false);
      console.log(`done 1`);
      console.log(`done 2`);
    }
  }, [setData, isFetching]);

  useEffect(() => {
    window.addEventListener("scroll", fetchData);
    return () => {
      window.removeEventListener("scroll", fetchData);
    };
  }, [fetchData]);

  return (
    <div className="App">
      <button
        onClick={() => {
          setData([]);
        }}
      >
        Clear
      </button>
      <h1 className="Data">Data: {data}</h1>
    </div>
  );
}

Code sandbox

When scrolling there is an uneven ratio of adjacent console.log prints, where you may see:

  • fetch 1
  • fetch 2
  • fetch 2
  • done 1
  • done 2

or some combination where 5 or more statements appear to make up one function call.

While React itself is not desynchronized, the strange output caused me to worry about lifecycle bugs in a complex project. Not to mention clouding the logs so much that they lost readability.

Note:

  • Refs do not help
  • Throttling/debouncing does not help
0

There are 0 best solutions below