Skip to content

Commit

Permalink
fix: handle unmount in useDebouncedCallback
Browse files Browse the repository at this point in the history
  • Loading branch information
thebuilder committed Jul 26, 2024
1 parent 7902355 commit 4423e55
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 3 deletions.
22 changes: 20 additions & 2 deletions src/__tests__/useDebouncedCallback.test.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,16 @@
import { renderHook } from "@testing-library/react";
import { beforeEach } from "vitest";
import { afterEach, beforeAll } from "vitest";
import { useDebouncedCallback } from "../hooks/useDebouncedCallback";

beforeEach(() => {
beforeAll(() => {
vi.useFakeTimers();
});

afterEach(() => {
// Should be no pending timers after each test
expect(vi.getTimerCount()).toBe(0);
});

test("should call the callback after the delay", async () => {
const cb = vi.fn();
const { result } = renderHook(() => useDebouncedCallback(cb, 500));
Expand Down Expand Up @@ -87,6 +92,7 @@ test("should handle leading option", async () => {
vi.advanceTimersToNextTimer();
result.current("c");
expect(cb).toHaveBeenCalledWith("c");
vi.advanceTimersToNextTimer();
});

test("should handle both leading and trailing option", async () => {
Expand All @@ -106,6 +112,18 @@ test("should handle both leading and trailing option", async () => {
expect(cb).toHaveBeenCalledWith("b");
});

test("should call stop pending callbacks on unmount", async () => {
const cb = vi.fn();
const { result, unmount } = renderHook(() => useDebouncedCallback(cb, 500));

result.current();
unmount();

// After unmounting, the callback should not be called and there should be no pending timers
expect(vi.getTimerCount()).toBe(0);
expect(cb).not.toHaveBeenCalled();
});

test("should infer the correct callback signature", async () => {
const cb = (value: string, count: number, opts: { input: string }) => {};
const { result } = renderHook(() => useDebouncedCallback(cb, 500));
Expand Down
9 changes: 8 additions & 1 deletion src/hooks/useDebouncedCallback.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { useMemo, useRef } from "react";
import { useEffect, useMemo, useRef } from "react";

type DebounceOptions = {
/**
Expand Down Expand Up @@ -60,6 +60,13 @@ export function useDebouncedCallback<
// This ensures that the user doesn't accidentally recreate the debounced function.
cb.current = func;

useEffect(() => {
return () => {
// Clear any pending timeouts when the hook unmounts
if (timeout.current) clearTimeout(timeout.current);
};
}, []);

return useMemo(() => {
let currentArgs: Parameters<T>;

Expand Down

0 comments on commit 4423e55

Please sign in to comment.