javascript 56 lines · 8 steps

Delaying a loading spinner with a React hook

A custom hook holds off showing a spinner until an operation runs long enough to warrant it, avoiding flicker on fast responses.

Explained by highlit
1import { useEffect, useRef, useState } from 'react';
2 
3export function useDelayedFlag(active, delay = 300) {
4 const [visible, setVisible] = useState(false);
5 const timeoutRef = useRef(null);
6 
7 useEffect(() => {
8 if (active) {
9 timeoutRef.current = setTimeout(() => setVisible(true), delay);
10 } else {
11 setVisible(false);
12 }
13 
14 return () => {
15 if (timeoutRef.current) {
16 clearTimeout(timeoutRef.current);
17 timeoutRef.current = null;
18 }
19 };
20 }, [active, delay]);
21 
22 return visible;
23}
24 
25export function UserSearchResults({ query }) {
26 const [results, setResults] = useState([]);
27 const [isFetching, setIsFetching] = useState(false);
28 const showSpinner = useDelayedFlag(isFetching, 250);
29 
30 useEffect(() => {
31 if (!query) return;
32 const controller = new AbortController();
33 setIsFetching(true);
34 
35 fetch(`/api/users?q=${encodeURIComponent(query)}`, { signal: controller.signal })
36 .then((res) => res.json())
37 .then((data) => setResults(data.users))
38 .catch((err) => {
39 if (err.name !== 'AbortError') throw err;
40 })
41 .finally(() => setIsFetching(false));
42 
43 return () => controller.abort();
44 }, [query]);
45 
46 return (
47 <div className="search-results">
48 {showSpinner && <Spinner label="Searching…" />}
49 <ul>
50 {results.map((user) => (
51 <li key={user.id}>{user.name}</li>
52 ))}
53 </ul>
54 </div>
55 );
56}
01 / 01
STEP 01

Walkthrough

Space play step click any line
Three takeaways
  1. 1Delaying a loading indicator prevents distracting flicker when work finishes faster than the human eye needs.
  2. 2Storing timers and controllers in cleanup-aware effects keeps async work from leaking across renders.
  3. 3Extracting timing logic into a reusable hook keeps components focused on rendering rather than bookkeeping.

Related explainers

Share this explainer

Here's the card — post it anywhere.

Delaying a loading spinner with a React hook — share card
Made with highlit — turn any snippet into a walkthrough like this in about a minute.
Explain your code