Skip to content

Commit

Permalink
Merge pull request #49 from atomicjolt/sc/useDebouncedState
Browse files Browse the repository at this point in the history
Implemented useDebouncedState
  • Loading branch information
seanrcollings authored Dec 2, 2024
2 parents 19e6aed + 63ac6ec commit f677a04
Show file tree
Hide file tree
Showing 3 changed files with 80 additions and 0 deletions.
13 changes: 13 additions & 0 deletions packages/hooks/src/hooks/useDebouncedState.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { useEffect, useState } from 'react';

export function useDebouncedState<T>(value: T, delay: number): ReturnType<typeof useState<T>> {
const [state, setState] = useState<T | undefined>(value);

useEffect(() => {
const handler = setTimeout(() => setState(value), delay);

return () => clearTimeout(handler);
}, [value, delay]);

return [state, setState];
}
1 change: 1 addition & 0 deletions packages/hooks/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,3 +7,4 @@ export { useRenderCount } from './hooks/lifecycle/useRenderCount';
export { useRender } from './hooks/lifecycle/useRender';
export { useUnmount } from './hooks/lifecycle/useUnmount';
export { useMount } from './hooks/lifecycle/useMount';
export { useDebouncedState } from './hooks/useDebouncedState';
66 changes: 66 additions & 0 deletions packages/hooks/tests/useDebouncedState.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
import { renderHook, act } from '@testing-library/react';
import { useDebouncedState } from '../src';
import { describe, it, expect, vi } from 'vitest';

describe('useDebouncedState', () => {
vi.useFakeTimers();

it('should initialize with the given value', () => {
const { result } = renderHook(() => useDebouncedState('initial', 500));
expect(result.current[0]).toBe('initial');
});

it('should update the state after the delay', () => {
const { result, rerender } = renderHook(({ value, delay }) => useDebouncedState(value, delay), {
initialProps: { value: 'initial', delay: 500 },
});

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

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

expect(result.current[0]).toBe('updated');
});

it('should not update the state before the delay', () => {
const { result, rerender } = renderHook(({ value, delay }) => useDebouncedState(value, delay), {
initialProps: { value: 'initial', delay: 500 },
});

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

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

expect(result.current[0]).toBe('initial');
});

it('should reset the timer if value changes before delay', () => {
const { result, rerender } = renderHook(({ value, delay }) => useDebouncedState(value, delay), {
initialProps: { value: 'initial', delay: 500 },
});

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

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

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

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

expect(result.current[0]).toBe('initial');

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

expect(result.current[0]).toBe('updated2');
});
});

0 comments on commit f677a04

Please sign in to comment.