Skip to content

Commit

Permalink
feat: useDebounce hook
Browse files Browse the repository at this point in the history
  • Loading branch information
congweibai committed Aug 20, 2024
1 parent acc2d52 commit 751616d
Show file tree
Hide file tree
Showing 4 changed files with 119 additions and 0 deletions.
1 change: 1 addition & 0 deletions src/hooks/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,3 +11,4 @@ export * from "./useTimeout";
export * from "./useWindowSize";
export * from "./useVisibilityChange";
export * from "./useObjectState";
export * from "./useDebounce";
1 change: 1 addition & 0 deletions src/hooks/useDebounce/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
export * from "./useDebounce";
100 changes: 100 additions & 0 deletions src/hooks/useDebounce/useDebounce.test.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { renderHook, act } from "@testing-library/react-hooks";
import useDebounce from "./useDebounce";
import { describe, it, expect, vi, beforeAll, afterAll } from "vitest";

describe("useDebounce", () => {
beforeAll(() => {
vi.useFakeTimers();
});

afterAll(() => {
vi.clearAllTimers();
});

it("should return the initial value immediately", () => {
// act
const { result } = renderHook(() => useDebounce("initial", 500));

// assert
expect(result.current).toBe("initial");
});

it("should debounce the value after the specified delay", async () => {
// act
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{
initialProps: { value: "initial", delay: 500 },
}
);

// act
expect(result.current).toBe("initial");

rerender({ value: "updated", delay: 500 });

expect(result.current).toBe("initial");

act(() => {
vi.advanceTimersByTime(500);
});

expect(result.current).toBe("updated");
});

it("should reset the debounce timer if the value changes before the delay", () => {
// act
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{
initialProps: { value: "initial", delay: 500 },
}
);

// assert
expect(result.current).toBe("initial");

rerender({ value: "updated1", delay: 500 });

act(() => {
vi.advanceTimersByTime(300);
});

expect(result.current).toBe("initial");

rerender({ value: "updated2", delay: 500 });

act(() => {
vi.advanceTimersByTime(500);
});

expect(result.current).toBe("updated2");
});

it("should handle delay changes correctly", () => {
// act
const { result, rerender } = renderHook(
({ value, delay }) => useDebounce(value, delay),
{
initialProps: { value: "initial", delay: 500 },
}
);

// assert
expect(result.current).toBe("initial");

rerender({ value: "updated", delay: 1000 });

act(() => {
vi.advanceTimersByTime(500);
});

expect(result.current).toBe("initial");

act(() => {
vi.advanceTimersByTime(500);
});

expect(result.current).toBe("updated");
});
});
17 changes: 17 additions & 0 deletions src/hooks/useDebounce/useDebounce.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
import { useEffect, useState } from "react";

export default function useDebounce(value: unknown, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);

useEffect(() => {
const id = window.setTimeout(() => {
setDebouncedValue(value);
}, delay);

return () => {
window.clearTimeout(id);
};
}, [value, delay]);

return debouncedValue;
}

0 comments on commit 751616d

Please sign in to comment.