Sign in
  1. Write a React hook that debounces a value and cancels on unmount. Include the TypeScript types.

    Claude Opus 5.5

    useDebouncedValue and useDebouncedCallback

    Below are two hooks:

    1. useDebouncedValue: debounces a value. This is what you asked for.

    2. useDebouncedCallback: debounces a function, with cancel and flush controls. It's useful when you need to debounce an event handler rather than state.

    Both cancel any pending timer when the component unmounts, so no state update or callback runs after unmount.

    1. useDebouncedValue

    ts
    // useDebouncedValue.ts
    import { useEffect, useState } from 'react';
    
    /**
     * Returns `value` after it has stopped changing for `delay` ms.
     * Any pending update is cancelled when `value` or `delay` changes,
     * or when the component unmounts.
     */
    export function useDebouncedValue<T>(value: T, delay: number = 300): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value);
    
      useEffect(() => {
        const timeoutId: ReturnType<typeof setTimeout> = setTimeout(() => {
          setDebouncedValue(value);
        }, delay);
    
        // Runs before the next effect (value/delay changed) AND on unmount.
        return () => {
          clearTimeout(timeoutId);
        };
      }, [value, delay]);
    
      return debouncedValue;
    }

    Usage

    tsx
    import { useEffect, useState } from 'react';
    import { useDebouncedValue } from './useDebouncedValue';
    
    interface User {
      id: string;
      name: string;
    }
    
    export function UserSearch() {
      const [query, setQuery] = useState<string>('');
      const [results, setResults] = useState<User[]>([]);
      const debouncedQuery = useDebouncedValue(query, 400);
    
      useEffect(() => {
        if (!debouncedQuery) {
          setResults([]);
          return;
        }
    
        const controller = new AbortController();
    
        fetch(`/api/users?q=${encodeURIComponent(debouncedQuery)}`, {
          signal: controller.signal,
        })
          .then((res) => res.json() as Promise<User[]>)
          .then(setResults)
          .catch((err: unknown) => {
            if ((err as Error).name !== 'AbortError') console.error(err);
          });
    
        return () => controller.abort(); // cancel in-flight request too
      }, [debouncedQuery]);
    
      return (
        <>
          <input value={query} onChange={(e) => setQuery(e.target.value)} />
          <ul>
            {results.map((u) => (
              <li key={u.id}>{u.name}</li>
            ))}
          </ul>
        </>
      );
    }

    2. useDebouncedCallback (with cancel / flush)

    ts
    // useDebouncedCallback.ts
    import { useEffect, useLayoutEffect, useMemo, useRef } from 'react';
    
    export interface DebouncedFunction<Args extends unknown[]> {
      (...args: Args): void;
      /** Cancel any pending invocation. */
      cancel: () => void;
      /** Immediately invoke the pending call, if any. */
      flush: () => void;
      /** Whether a call is currently scheduled. */
      isPending: () => boolean;
    }
    
    // useLayoutEffect warns during SSR; fall back to useEffect on the server.
    const useIsomorphicLayoutEffect =
      typeof window !== 'undefined' ? useLayoutEffect : useEffect;
    
    export function useDebouncedCallback<Args extends unknown[]>(
      callback: (...args: Args) => void,
      delay: number = 300
    ): DebouncedFunction<Args> {
      const callbackRef = useRef(callback);
      const timeoutRef = useRef<ReturnType<typeof setTimeout> | null>(null);
      const pendingArgsRef = useRef<Args | null>(null);
    
      // Always call the latest callback without resetting the debounce timer.
      useIsomorphicLayoutEffect(() => {
        callbackRef.current = callback;
      }, [callback]);
    
      const debounced = useMemo<DebouncedFunction<Args>>(() => {
        const invoke = (): void => {
          const args = pendingArgsRef.current;
          timeoutRef.current = null;
          pendingArgsRef.current = null;
          if (args) callbackRef.current(...args);
        };
    
        const cancel = (): void => {
          if (timeoutRef.current !== null) {
            clearTimeout(timeoutRef.current);
            timeoutRef.current = null;
          }
          pendingArgsRef.current = null;
        };
    
        const fn = ((...args: Args): void => {
          pendingArgsRef.current = args;
          if (timeoutRef.current !== null) clearTimeout(timeoutRef.current);
          timeoutRef.current = setTimeout(invoke, delay);
        }) as DebouncedFunction<Args>;
    
        fn.cancel = cancel;
        fn.flush = () => {
          if (timeoutRef.current !== null) {
            clearTimeout(timeoutRef.current);
            invoke();
          }
        };
        fn.isPending = () => timeoutRef.current !== null;
    
        return fn;
      }, [delay]);
    
      // Cancel on unmount (and when `delay` changes, which creates a new function).
      useEffect(() => {
        return () => debounced.cancel();
      }, [debounced]);
    
      return debounced;
    }

    Usage

    tsx
    import { useDebouncedCallback } from './useDebouncedCallback';
    
    export function AutoSaveEditor({ docId }: { docId: string }) {
      const save = useDebouncedCallback((content: string) => {
        void fetch(`/api/docs/${docId}`, {
          method: 'PUT',
          body: JSON.stringify({ content }),
        });
      }, 1000);
    
      return (
        <textarea
          onChange={(e) => save(e.target.value)} // typed: (content: string) => void
          onBlur={() => save.flush()}            // save immediately when leaving
        />
      );
    }

    Notes

    • ReturnType<typeof setTimeout> keeps the timer type correct in both browser (number) and Node (Timeout) environments.

    • Cancellation on unmount comes from the effect cleanup functions. React calls them on unmount, so clearTimeout stops any pending setState or callback.

    • Stable callback identity: useDebouncedCallback stores the latest callback in a ref. Passing an inline arrow function therefore doesn't reset the timer on every render. The returned function only changes when delay changes.

    • StrictMode double-mounts effects in development. Both hooks handle this correctly because cleanup clears the timer before the effect runs again.

    Asked Nov 1, 2026. Unedited.

    See Claude Opus 5.5
  2. Write a React hook that debounces a value and cancels on unmount. Include the TypeScript types.

    Gemini 4 Argon

    Here is a clean, generic useDebounce hook written in TypeScript. It automatically handles canceling the timeout both when the value/delay changes and when the component unmounts.

    useDebounce.ts

    typescript
    import { useState, useEffect } from 'react';
    
    /**
     * Custom hook that debounces a value.
     *
     * @param value The value to debounce.
     * @param delay The delay in milliseconds.
     * @returns The debounced value.
     */
    export function useDebounce<T>(value: T, delay: number): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value);
    
      useEffect(() => {
        // Set a timeout to update the debounced value after the specified delay
        const timer = setTimeout(() => {
          setDebouncedValue(value);
        }, delay);
    
        // Cleanup function: cancels the timeout if the value or delay changes
        // before the timeout completes, OR if the component unmounts.
        return () => {
          clearTimeout(timer);
        };
      }, [value, delay]);
    
      return debouncedValue;
    }

    Example Usage

    Here is how you can use it in a component (e.g., waiting for a user to stop typing before making an API call):

    tsx
    import React, { useState, useEffect } from 'react';
    import { useDebounce } from './useDebounce';
    
    export const SearchComponent: React.FC = () => {
      const [searchTerm, setSearchTerm] = useState<string>('');
      
      // Debounce the search term so it only updates 500ms after the user stops typing
      const debouncedSearchTerm = useDebounce<string>(searchTerm, 500);
    
      useEffect(() => {
        if (debouncedSearchTerm) {
          console.log(`Fetching API results for: ${debouncedSearchTerm}`);
          // Perform your API call here
        }
      }, [debouncedSearchTerm]);
    
      return (
        <input
          type="text"
          placeholder="Search..."
          value={searchTerm}
          onChange={(e) => setSearchTerm(e.target.value)}
        />
      );
    };

    Key Features:

    1. Generics (<T>): It preserves the exact TypeScript type of whatever value you pass into it (strings, numbers, objects, arrays, etc.).

    2. Unmount Safety: Because clearTimeout is returned in the useEffect cleanup function, React guarantees it will run if the component unmounts, preventing memory leaks and "state update on an unmounted component" warnings.

    Asked Nov 1, 2026. Unedited.

    See Gemini 4 Argon
  3. Write a React hook that debounces a value and cancels on unmount. Include the TypeScript types.

    Claude Fable 5.1

    Here's a typed useDebounce hook. It returns the debounced value and clears the pending timer whenever the input changes or the component unmounts, so you never get a state update on an unmounted component.

    tsx
    import { useEffect, useState } from "react";
    
    /**
     * Returns a debounced copy of `value` that only updates after `delayMs`
     * milliseconds have passed without `value` changing.
     *
     * The pending update is cancelled when `value` or `delayMs` changes,
     * and when the component unmounts.
     */
    export function useDebounce<T>(value: T, delayMs: number = 300): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value);
    
      useEffect(() => {
        const timeoutId: ReturnType<typeof setTimeout> = setTimeout(() => {
          setDebouncedValue(value);
        }, delayMs);
    
        // Cleanup runs before the next effect and on unmount.
        return () => {
          clearTimeout(timeoutId);
        };
      }, [value, delayMs]);
    
      return debouncedValue;
    }

    Usage

    tsx
    function Search() {
      const [query, setQuery] = useState("");
      const debouncedQuery = useDebounce(query, 400);
    
      useEffect(() => {
        if (!debouncedQuery) return;
        // Fire the request only after the user pauses typing for 400ms.
        fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`);
      }, [debouncedQuery]);
    
      return (
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search…"
        />
      );
    }

    Notes

    • Generic T means it works for strings, numbers, objects, etc., with full type inference at the call site.

    • ReturnType<typeof setTimeout> keeps the timer ID type correct in both browser (number) and Node (Timeout) environments.

    • Cancellation on unmount comes for free from the effect cleanup — React calls it when the component unmounts, so setDebouncedValue is never invoked afterward.

    • If you need an immediate update when delayMs is 0, you can short-circuit at the top of the effect with if (delayMs <= 0) { setDebouncedValue(value); return; }.

    Asked Nov 1, 2026. Unedited.

    See Claude Fable 5.1
  4. Write a React hook that debounces a value and cancels on unmount. Include the TypeScript types.

    Claude Sonnet 5.5

    useDebounce hook

    ts
    // useDebounce.ts
    import { useEffect, useState } from "react";
    
    /**
     * Returns a debounced copy of `value` that only updates after `delay` ms
     * have passed without `value` changing.
     *
     * The pending timer is cleared when `value` or `delay` changes,
     * and when the component unmounts.
     */
    export function useDebounce<T>(value: T, delay: number = 300): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value);
    
      useEffect(() => {
        const timeoutId: ReturnType<typeof setTimeout> = setTimeout(() => {
          setDebouncedValue(value);
        }, delay);
    
        // Runs before the next effect and on unmount
        return () => clearTimeout(timeoutId);
      }, [value, delay]);
    
      return debouncedValue;
    }

    Usage

    tsx
    import { useEffect, useState } from "react";
    import { useDebounce } from "./useDebounce";
    
    export function Search() {
      const [query, setQuery] = useState("");
      const debouncedQuery = useDebounce(query, 500);
    
      useEffect(() => {
        if (!debouncedQuery) return;
    
        const controller = new AbortController();
    
        fetch(`/api/search?q=${encodeURIComponent(debouncedQuery)}`, {
          signal: controller.signal,
        }).catch(() => {});
    
        return () => controller.abort();
      }, [debouncedQuery]);
    
      return (
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search…"
        />
      );
    }

    Why this cancels correctly

    • The effect's cleanup function calls clearTimeout. React runs it before re-running the effect (so each new value restarts the timer) and on unmount (so setDebouncedValue never fires on an unmounted component).

    • ReturnType<typeof setTimeout> keeps the timer type correct in both browser (number) and Node (NodeJS.Timeout) typings.


    Optional: version with cancel and flush

    If you need manual control (e.g. flush on Enter, cancel on Escape):

    ts
    // useDebouncedValue.ts
    import { useCallback, useEffect, useRef, useState } from "react";
    
    export interface DebounceControls {
      /** Cancel the pending update, if any. */
      cancel: () => void;
      /** Apply the latest value immediately and cancel the pending timer. */
      flush: () => void;
    }
    
    export function useDebouncedValue<T>(
      value: T,
      delay: number = 300
    ): [T, DebounceControls] {
      const [debounced, setDebounced] = useState<T>(value);
      const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
      const latestValue = useRef<T>(value);
      latestValue.current = value;
    
      const cancel = useCallback(() => {
        if (timerRef.current !== null) {
          clearTimeout(timerRef.current);
          timerRef.current = null;
        }
      }, []);
    
      const flush = useCallback(() => {
        cancel();
        setDebounced(latestValue.current);
      }, [cancel]);
    
      useEffect(() => {
        cancel();
        timerRef.current = setTimeout(() => {
          timerRef.current = null;
          setDebounced(latestValue.current);
        }, delay);
    
        return cancel; // cancels on change and on unmount
      }, [value, delay, cancel]);
    
      return [debounced, { cancel, flush }];
    }
    tsx
    const [debounced, { flush, cancel }] = useDebouncedValue(query, 500);
    
    <input
      value={query}
      onChange={(e) => setQuery(e.target.value)}
      onKeyDown={(e) => {
        if (e.key === "Enter") flush();
        if (e.key === "Escape") cancel();
      }}
    />

    Asked Nov 1, 2026. Unedited.

    See Claude Sonnet 5.5
  5. Write a React hook that debounces a value and cancels on unmount. Include the TypeScript types.

    MiMo V2.6 Pro

    typescript
    import { useEffect, useState, useRef } from 'react';
    
    /**
     * Debounces a value so it only updates after a specified delay of
     * no further changes. The pending timer is cancelled on unmount.
     *
     * @template T  The type of the value being debounced.
     * @param value     The value to debounce.
     * @param delayMs   Delay in milliseconds (default: 300).
     * @returns The debounced value (updates only after `delayMs` of silence).
     */
    function useDebouncedValue<T>(value: T, delayMs: number = 300): T {
      const [debouncedValue, setDebouncedValue] = useState<T>(value);
      const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
    
      useEffect(() => {
        // Cancel any pending timeout when value changes or on unmount
        if (timerRef.current !== null) {
          clearTimeout(timerRef.current);
        }
    
        timerRef.current = setTimeout(() => {
          setDebouncedValue(value);
        }, delayMs);
    
        // Cleanup runs on every re-run (value change) AND on unmount
        return () => {
          if (timerRef.current !== null) {
            clearTimeout(timerRef.current);
          }
        };
      }, [value, delayMs]);
    
      return debouncedValue;
    }
    
    export default useDebouncedValue;

    How it works

    Table
    ConcernMechanism
    DebounceEvery time value changes, a new setTimeout is scheduled. The state is only updated after delayMs has elapsed with no further changes.
    Cancel on unmountThe useEffect cleanup function clears the pending timeout. This cleanup runs on every dependency change and when the component unmounts.
    Cancel on rapid changesBecause the previous timer is cleared before creating a new one, rapid successive changes never fire stale updates.
    Type safetyThe generic T propagates from the input value to the returned value — no casting needed.

    Usage example

    tsx
    import { useState } from 'react';
    import useDebouncedValue from './useDebouncedValue';
    
    function SearchBar() {
      const [query, setQuery] = useState('');
      const debouncedQuery = useDebouncedValue(query, 500);
    
      // Only runs when the user pauses typing for 500 ms
      useEffect(() => {
        if (!debouncedQuery) return;
        fetchResults(debouncedQuery);
      }, [debouncedQuery]);
    
      return (
        <input
          value={query}
          onChange={(e) => setQuery(e.target.value)}
          placeholder="Search…"
        />
      );
    }

    Optional: debounce a callback instead

    If you need to debounce a function call rather than a value (e.g. an event handler), here is a companion hook that also cancels on unmount:

    typescript
    import { useEffect, useRef, useCallback } from 'react';
    
    function useDebouncedCallback<A extends unknown[]>(
      callback: (...args: A) => void,
      delayMs: number = 300
    ): (...args: A) => void {
      const timerRef = useRef<ReturnType<typeof setTimeout> | null>(null);
      const callbackRef = useRef(callback);
    
      // Keep the latest callback without re-creating the debounce timer
      useEffect(() => {
        callbackRef.current = callback;
      }, [callback]);
    
      // Cancel pending call on unmount
      useEffect(() => {
        return () => {
          if (timerRef.current !== null) {
            clearTimeout(timerRef.current);
          }
        };
      }, []);
    
      return useCallback(
        (...args: A) => {
          if (timerRef.current !== null) {
            clearTimeout(timerRef.current);
          }
          timerRef.current = setTimeout(() => {
            callbackRef.current(...args);
          }, delayMs);
        },
        [delayMs]
      );
    }

    This variant uses callbackRef to always invoke the latest version of the callback while keeping the debounce timer stable, and tears down the timer in a dedicated unmount cleanup.

    Asked Nov 1, 2026. Unedited.

    See MiMo V2.6 Pro

Top 10 for coding

Scores from artificialanalysis.ai and lmarena.ai.

API price is per 1M tokens, in / out. Updated Nov 2, 2026.

The 10 models that rank best for coding
#ModelArtificial AnalysisLMArena codingAPI price
1Claude Opus 5.557.61,545$4 / $20
2Gemini 4 Argon——$2 / $10
3Claude Fable 5.153.41,531$10 / $50
4Claude Sonnet 5.556.01,510$2 / $10
5MiMo V2.6 Pro46.31,533$0.43 / $0.87
6GPT-6 Astra52.7—$10 / $50
7Muse Spark 1.348.11,511$1.25 / $4.25
8GPT-6.1 Sol51.81,482$2 / $10
9ChatGPT 5.6 Sol47.01,496$4 / $20
10Qwen 3.8 Max45.41,503$2 / $6

We rank every model per task, blending public leaderboards with our own blind tests where we have them. Price and speed don’t count toward the rank. How we rank

Keep exploring

Ready when you are.

Try Claude Opus 5.5