Skip to content

Commit

Permalink
Merge branch 'main' of github.com:vintach/raddix into feat/debounced-…
Browse files Browse the repository at this point in the history
…hooks
  • Loading branch information
immois committed Oct 19, 2023
2 parents 493657f + b32efb9 commit 195bb28
Show file tree
Hide file tree
Showing 9 changed files with 106 additions and 34 deletions.
6 changes: 6 additions & 0 deletions packages/hooks/use-interval/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @raddix/use-interval

## 1.1.0

### Minor Changes

- 0ec61b2: Add null type in the delay option

## 1.0.0

### Major Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/hooks/use-interval/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@raddix/use-interval",
"description": "A React hook for setting an interval.",
"version": "1.0.0",
"version": "1.1.0",
"license": "MIT",
"main": "src/index.ts",
"author": "Moises Machuca Valverde <rolan.machuca@gmail.com> (https://www.moisesmachuca.com)",
Expand Down
28 changes: 9 additions & 19 deletions packages/hooks/use-interval/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,23 @@
import { useCallback, useEffect, useRef } from 'react';

export interface IntervalResult {
/** Clear interval and stop the execution */
clear: () => void;
/** Execute the interval */
run: () => void;
}

type UseInterval = (
export const useInterval = (
callback: () => void,
delay: number,
inmediate?: boolean
) => IntervalResult;

export const useInterval: UseInterval = (callback, delay, inmediate = true) => {
delay: number | null,
inmediate = true
): { clear: () => void; run: () => void } => {
const savedCallback = useRef(callback);
const id = useRef<NodeJS.Timeout | null>(null);

const clearId = () => {
const clear = () => {
if (!id.current) return;
clearInterval(id.current);
id.current = null;
};

const run = useCallback(() => {
if (id.current) return;
const tick = () => savedCallback.current();

id.current = setInterval(tick, delay);
if (delay === null) return;
id.current = setInterval(() => savedCallback.current(), delay);
}, [delay]);

useEffect(() => {
Expand All @@ -36,8 +26,8 @@ export const useInterval: UseInterval = (callback, delay, inmediate = true) => {

useEffect(() => {
if (inmediate) run();
return () => clearId();
return () => clear();
}, [run, inmediate]);

return { clear: clearId, run };
return { clear, run };
};
8 changes: 8 additions & 0 deletions packages/hooks/use-interval/tests/use-interval.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,14 @@ describe('useInterval test:', () => {
expect(callback).toHaveBeenCalledTimes(1);
});

it('should not execute the callback if the delay is null', () => {
const callback = jest.fn();
renderHook(() => useInterval(callback, null));

jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
});

it('should clear the interval when the immediate changes', () => {
const callback = jest.fn();
const { rerender } = renderHook(
Expand Down
6 changes: 6 additions & 0 deletions packages/hooks/use-timeout/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,11 @@
# @raddix/use-timeout

## 1.1.0

### Minor Changes

- 70010d0: Added two new return functions: `reset` and `run`

## 1.0.0

### Major Changes
Expand Down
2 changes: 1 addition & 1 deletion packages/hooks/use-timeout/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,4 @@

The `useTimeout` hook is used to execute a callback after a specified amount of time. It is similar to the `setTimeout` function, but it is declarative and can be cancelled.

Please refer to the [documentation](https://www.raddix.website/docs/interactions/use-timeout) for more information.
Please refer to the [documentation](https://raddix.website/hooks/use-timeout) for more information.
2 changes: 1 addition & 1 deletion packages/hooks/use-timeout/package.json
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
{
"name": "@raddix/use-timeout",
"description": "A React hook for handling timeouts",
"version": "1.0.0",
"version": "1.1.0",
"license": "MIT",
"main": "src/index.ts",
"author": "Moises Machuca Valverde <rolan.machuca@gmail.com> (https://www.moisesmachuca.com)",
Expand Down
41 changes: 30 additions & 11 deletions packages/hooks/use-timeout/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,45 @@
import { useEffect, useRef } from 'react';
import { useCallback, useEffect, useRef } from 'react';

type UseTimeout = (cb: () => void, delay: number) => () => void;

export const useTimeout: UseTimeout = (cb, delay) => {
/**
* A React hook for handling timeouts
* @param cb The callback function to be executed after the specified timeout.
* @param delay The duration of the timeout in milliseconds.
* @param immediate If it is true, the callback is executed immediately.
* @see https://raddix.website/hooks/use-timeout
*/
export const useTimeout = (
cb: () => void,
delay: number | null,
immediate = true
): { clear: () => void; reset: () => void; run: () => void } => {
const savedCallback = useRef(cb);
const id = useRef<NodeJS.Timeout | null>(null);

const clearId = () => {
const clear = useCallback(() => {
if (!id.current) return;
clearTimeout(id.current);
id.current = null;
};
}, []);

const run = useCallback(() => {
if (id.current) return;
if (delay === null) return;
id.current = setTimeout(() => savedCallback.current(), delay);
}, [delay]);

const reset = useCallback(() => {
clear();
run();
}, [run, clear]);

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

useEffect(() => {
const tick = () => savedCallback.current();
id.current = setTimeout(tick, delay);
return () => clearId();
}, [delay]);
if (immediate) run();
return () => clear();
}, [delay, run, clear, immediate]);

return clearId;
return { clear, reset, run };
};
45 changes: 44 additions & 1 deletion packages/hooks/use-timeout/tests/use-timeout.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -46,14 +46,57 @@ describe('useTimeout test:', () => {
expect(callback).toHaveBeenCalledTimes(1);
});

it('should not execute the callback if the delay is null', () => {
const callback = jest.fn();
renderHook(() => useTimeout(callback, null));

jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
});

it('should not execute the callback immediately if the inmediate option is false', () => {
const callback = jest.fn();
renderHook(() => useTimeout(callback, 1000, false));

jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
});

it('should stop the timeout when the clear function is called', () => {
const callback = jest.fn();
const { result } = renderHook(() => useTimeout(callback, 4000));

expect(callback).not.toBeCalled();
jest.advanceTimersByTime(2000);
result.current();
result.current.clear();
jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
});

it('should restart the timeout when the reset function is called', () => {
const callback = jest.fn();
const { result } = renderHook(() => useTimeout(callback, 4000));

expect(callback).not.toBeCalled();
jest.advanceTimersByTime(2000);
result.current.reset();
jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
jest.advanceTimersByTime(2000);
expect(callback).toHaveBeenCalledTimes(1);
});

it('should execute the callback if the run function is called', () => {
const callback = jest.fn();
const { result } = renderHook(() => useTimeout(callback, 2000, false));

jest.advanceTimersByTime(2000);
expect(callback).not.toBeCalled();
result.current.run();
jest.advanceTimersByTime(4000);
expect(callback).toHaveBeenCalledTimes(1);
result.current.run();
jest.advanceTimersByTime(2000);
expect(callback).toHaveBeenCalledTimes(1);
});
});

0 comments on commit 195bb28

Please sign in to comment.