Skip to content

Commit

Permalink
rehook useInterval (#60)
Browse files Browse the repository at this point in the history
  • Loading branch information
BRIHMAT-Naoui authored Sep 7, 2024
1 parent 24fde95 commit 9078d52
Show file tree
Hide file tree
Showing 4 changed files with 179 additions and 3 deletions.
6 changes: 3 additions & 3 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
"@commitlint/cli": "^18.4.3",
"@commitlint/config-conventional": "^18.4.3",
"@testing-library/jest-dom": "^6.1.5",
"@testing-library/react": "^14.1.2",
"@testing-library/react": "^14.3.1",
"@trivago/prettier-plugin-sort-imports": "^4.3.0",
"@types/jest": "^29.5.12",
"@types/react": "^18.2.42",
Expand All @@ -35,12 +35,12 @@
"eslint-plugin-react-hooks": "^4.6.0",
"eslint-plugin-react-refresh": "^0.4.5",
"husky": "^8.0.3",
"vitest-fetch-mock": "^0.2.2",
"jsdom": "^24.0.0",
"lint-staged": "^15.2.0",
"prettier": "3.0.3",
"typescript": "^5.3.3",
"vite": "^4.5.3",
"vitest": "^1.6.0"
"vitest": "^1.6.0",
"vitest-fetch-mock": "^0.2.2"
}
}
27 changes: 27 additions & 0 deletions src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,32 @@ import './App.css'
import { useQueryParams } from './hooks/useQueryParams'
// import reactLogo from './assets/react.svg';
import { useState } from 'react'
import React from 'react';
import { useInterval } from './hooks/use-interval';

const Counter: React.FC = () => {
const [count, setCount] = React.useState<number>(0);

const incrementCount = (): void => {
setCount(prevCount => prevCount + 1);
};

const { isRunning, delay, startInterval, stopInterval, updateDelay } = useInterval(incrementCount, 1000);

return (
<div>
<p>Count: {count}</p>
<p>Interval: {delay}ms</p>
<p>Status: {isRunning ? 'Running' : 'Stopped'}</p>
<button onClick={startInterval}>Start</button>
<button onClick={stopInterval}>Stop</button>
<button onClick={() => updateDelay(500)}>Speed Up (500ms)</button>
<button onClick={() => updateDelay(2000)}>Slow Down (2000ms)</button>
</div>
);
};



function App() {
const [count, setCount] = useState(0)
Expand All @@ -12,6 +38,7 @@ function App() {

return (
<>
<Counter/>
<div>
<p>Current page: {get('page') || 'N/A'}</p>
<button onClick={handleClick}>Go to page 2</button>
Expand Down
100 changes: 100 additions & 0 deletions src/hooks/__tests__/useInterval.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
import { renderHook, act } from '@testing-library/react'
import { useInterval } from '../use-interval'
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest'

describe('useInterval', () => {
beforeEach(() => {
vi.useFakeTimers()
})

afterEach(() => {
vi.restoreAllMocks()
})

it('should call the callback function at the specified interval', () => {
const callback = vi.fn()
const { result } = renderHook(() => useInterval(callback, 1000))

expect(callback).not.toBeCalled()

act(() => {
vi.advanceTimersByTime(1000)
})
expect(callback).toHaveBeenCalledTimes(1)

act(() => {
vi.advanceTimersByTime(1000)
})
expect(callback).toHaveBeenCalledTimes(2)
})

it('should stop and start the interval', () => {
const callback = vi.fn()
const { result } = renderHook(() => useInterval(callback, 1000))

act(() => {
vi.advanceTimersByTime(1000)
})
expect(callback).toHaveBeenCalledTimes(1)

act(() => {
result.current.stopInterval()
})

act(() => {
vi.advanceTimersByTime(2000)
})
expect(callback).toHaveBeenCalledTimes(1)

act(() => {
result.current.startInterval()
})

act(() => {
vi.advanceTimersByTime(1000)
})
expect(callback).toHaveBeenCalledTimes(2)
})

it('should update the delay when updateDelay is called', () => {
const callback = vi.fn()
const { result } = renderHook(() => useInterval(callback, 1000))

act(() => {
vi.advanceTimersByTime(1000)
})
expect(callback).toHaveBeenCalledTimes(1)

act(() => {
result.current.updateDelay(500)
})

act(() => {
vi.advanceTimersByTime(500)
})
expect(callback).toHaveBeenCalledTimes(2)
})

it('should provide correct isRunning and delay values', () => {
const callback = vi.fn()
const { result } = renderHook(() => useInterval(callback, 1000))

expect(result.current.isRunning).toBe(true)
expect(result.current.delay).toBe(1000)

act(() => {
result.current.stopInterval()
})
expect(result.current.isRunning).toBe(false)

act(() => {
result.current.updateDelay(2000)
})
expect(result.current.delay).toBe(2000)

act(() => {
result.current.startInterval()
})
expect(result.current.isRunning).toBe(true)
})
})
49 changes: 49 additions & 0 deletions src/hooks/use-interval.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { useEffect, useRef, useState, useCallback } from 'react';

type IntervalFunction = () => void;

interface IntervalHookResult {
isRunning: boolean;
delay: number;
startInterval: () => void;
stopInterval: () => void;
updateDelay: (newDelay: number) => void;
}

export const useInterval = (initialCallback: IntervalFunction, initialDelay: number): IntervalHookResult => {
const [isRunning, setIsRunning] = useState<boolean>(true);
const [delay, setDelay] = useState<number>(initialDelay);
const savedCallback = useRef<IntervalFunction>(initialCallback);

useEffect(() => {
savedCallback.current = initialCallback;
}, [initialCallback]);

useEffect(() => {
if (!isRunning) return undefined;

const id = setInterval(() => savedCallback.current(), delay);

return () => clearInterval(id);
}, [delay, isRunning]);

const startInterval = useCallback((): void => {
setIsRunning(true);
}, []);

const stopInterval = useCallback((): void => {
setIsRunning(false);
}, []);

const updateDelay = useCallback((newDelay: number): void => {
setDelay(newDelay);
}, []);

return {
isRunning,
delay,
startInterval,
stopInterval,
updateDelay,
};
};

0 comments on commit 9078d52

Please sign in to comment.