Debounce

const debounce = (fn, wait) => {
let timeoutId = null;
return (...args) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
fn.apply(null, args);
}, wait);
}
}

Debouncing is a technique that let us restrict the calling of a function by delaying the execution that function until a certain amount of time has passed.

A debounce function makes sure that our code, triggered by events in most cases, is only triggered once in a certain amount of time. When the user stops triggering the event, the code starts to run. The common use cases are search box suggestions, text field inputs, and eliminating double button clicks.

Updated version from nodejs.org

type DebounceFunction<T = unknown> = (...args: Array<T>) => void;
let timeoutId: NodeJS.Timeout;
export const debounce =
<T extends DebounceFunction>(
func: T,
delay: number
): ((...args: Parameters<T>) => void) =>
(...args: Parameters<T>) => {
clearTimeout(timeoutId);
timeoutId = setTimeout(() => {
func(...args);
}, delay);
};

While using func(...args) is simpler and would work for many basic functions, we can preserve the this context of the original function to make the debounce utility more robust and handle critical edge cases:

import React, { useState, useCallback } from 'react';
/**
* A generic type for any function.
*/
type AnyFunction = (...args: any[]) => any;
/**
* Creates a debounced function that delays invoking `func` until after `delay` milliseconds
* have passed since the last time the debounced function was invoked.
*
* @param func The function to debounce.
* @param delay The number of milliseconds to delay.
* @returns A new debounced function.
*/
function debounce<F extends AnyFunction>(func: F, delay: number): (...args: Parameters<F>) => void {
let timer: ReturnType<typeof setTimeout> | null = null;
return function(this: ThisParameterType<F>, ...args: Parameters<F>) {
const context = this;
if (timer) {
clearTimeout(timer);
}
timer = setTimeout(() => {
func.apply(context, args);
}, delay);
};
}