-
Notifications
You must be signed in to change notification settings - Fork 99
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
13 changed files
with
291 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import * as React from 'react'; | ||
import { useState } from 'react'; | ||
import { useDebouncedEffect } from '../..'; | ||
|
||
const HAS_DIGIT_REGEX = /[\d]/g; | ||
|
||
export const Example: React.FC = () => { | ||
const [state, setState] = useState(''); | ||
const [hasNumbers, setHasNumbers] = useState(false); | ||
|
||
useDebouncedEffect( | ||
() => { | ||
setHasNumbers(HAS_DIGIT_REGEX.test(state)); | ||
}, | ||
[state], | ||
200, | ||
500 | ||
); | ||
|
||
return ( | ||
<div> | ||
<div> | ||
Digit check will be performed 200ms after last change, but at least once every 500ms | ||
</div> | ||
<br /> | ||
<div>{hasNumbers ? 'Input has digits' : 'No digits found in input'}</div> | ||
<input | ||
type="text" | ||
value={state} | ||
onChange={(ev) => { | ||
setState(ev.target.value); | ||
}} | ||
/> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,35 @@ | ||
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks'; | ||
import { Example } from './example.stories'; | ||
|
||
<Meta title="Lifecycle/useDebouncedEffect" component={Example} /> | ||
|
||
# useDebouncedEffect | ||
|
||
Like `useEffect`, but passed function is debounced. | ||
|
||
#### Example | ||
|
||
<Canvas> | ||
<Story story={Example} inline /> | ||
</Canvas> | ||
|
||
## Reference | ||
|
||
```ts | ||
export function useDebouncedEffect( | ||
callback: (...args: any[]) => void, | ||
deps: DependencyList, | ||
delay: number, | ||
maxWait = 0 | ||
): void; | ||
``` | ||
|
||
#### Arguments | ||
|
||
- **callback** _`(...args: any[]) => void`_ - Callback like for `useEffect`, but without ability to | ||
return a cleanup function. | ||
- **deps** _`DependencyList`_ - Dependencies list that will be passed to underlying `useEffect` and | ||
`useDebouncedCallback`. | ||
- **delay** _`number`_ - Debounce delay. | ||
- **maxWait** _`number`_ _(default: `0`)_ The maximum time `callback` is allowed to be delayed | ||
before it's invoked. `0` means no max wait. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
import { renderHook } from '@testing-library/react-hooks/dom'; | ||
import { useDebouncedEffect } from '../..'; | ||
|
||
describe('useDebouncedEffect', () => { | ||
beforeAll(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useDebouncedEffect).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useDebouncedEffect(() => {}, [], 200)); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
|
||
it('should call effect only after delay', () => { | ||
const spy = jest.fn(); | ||
|
||
renderHook(() => useDebouncedEffect(spy, [], 200)); | ||
expect(spy).not.toHaveBeenCalled(); | ||
|
||
jest.advanceTimersByTime(199); | ||
expect(spy).not.toHaveBeenCalled(); | ||
|
||
jest.advanceTimersByTime(1); | ||
expect(spy).toHaveBeenCalledTimes(1); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { renderHook } from '@testing-library/react-hooks/server'; | ||
import { useDebouncedEffect } from '../..'; | ||
|
||
describe('useDebouncedEffect', () => { | ||
beforeAll(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useDebouncedEffect).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useDebouncedEffect(() => {}, [], 200)); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
import { DependencyList, useEffect } from 'react'; | ||
import { useDebouncedCallback } from '..'; | ||
|
||
/** | ||
* Like `useEffect`, but passed function is debounced. | ||
* | ||
* @param callback Callback like for `useEffect`, but without ability to return | ||
* a cleanup function. | ||
* @param deps Dependencies list that will be passed to underlying `useEffect` | ||
* and `useDebouncedCallback`. | ||
* @param delay Debounce delay. | ||
* @param maxWait Maximum amount of milliseconds that function can be delayed | ||
* before it's force execution. 0 means no max wait. | ||
*/ | ||
export function useDebouncedEffect( | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
callback: (...args: any[]) => void, | ||
deps: DependencyList, | ||
delay: number, | ||
maxWait = 0 | ||
): void { | ||
// eslint-disable-next-line react-hooks/exhaustive-deps | ||
useEffect(useDebouncedCallback(callback, deps, delay, maxWait), deps); | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import * as React from 'react'; | ||
import { useDebouncedState } from '../..'; | ||
|
||
export const Example: React.FC = () => { | ||
const [state, setState] = useDebouncedState('', 300, 500); | ||
|
||
return ( | ||
<div> | ||
<div>Below state will update 200ms after last change, but at least once every 500ms</div> | ||
<br /> | ||
<div>The input`s value is: {state}</div> | ||
<input | ||
type="text" | ||
onChange={(ev) => { | ||
setState(ev.target.value); | ||
}} | ||
/> | ||
</div> | ||
); | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { Canvas, Meta, Story } from '@storybook/addon-docs/blocks'; | ||
import { Example } from './example.stories'; | ||
|
||
<Meta title="State/useDebouncedState" component={Example} /> | ||
|
||
# useDebouncedState | ||
|
||
Lise `useSafeState` but its state setter is debounced. | ||
|
||
#### Example | ||
|
||
<Canvas> | ||
<Story story={Example} inline /> | ||
</Canvas> | ||
|
||
## Reference | ||
|
||
```ts | ||
export function useDebouncedState<S>( | ||
initialState: S | (() => S), | ||
delay: number, | ||
maxWait = 0 | ||
): [S, Dispatch<SetStateAction<S>>]; | ||
``` | ||
|
||
#### Arguments | ||
|
||
- **initialState** _`S | (() => S)`_ - Initial state to pass to underlying `useSafeState`. | ||
- **delay** _`number`_ - Debounce delay. | ||
- **maxWait** _`number`_ _(default: `0`)_ - The maximum time `callback` is allowed to be delayed | ||
before it's invoked. `0` means no max wait. | ||
|
||
#### Return | ||
|
||
0. **state** - current state. | ||
1. **setState** - debounced state setter. |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
import { act, renderHook } from '@testing-library/react-hooks/dom'; | ||
import { useDebouncedState } from '../..'; | ||
|
||
describe('useDebouncedState', () => { | ||
beforeAll(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useDebouncedState).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useDebouncedState(undefined, 200)); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
|
||
it('should ', () => { | ||
act(() => { | ||
const { result } = renderHook(() => useDebouncedState<string | undefined>(undefined, 200)); | ||
|
||
expect(result.current[0]).toBe(undefined); | ||
result.current[1]('Hello world!'); | ||
|
||
jest.advanceTimersByTime(199); | ||
expect(result.current[0]).toBe(undefined); | ||
|
||
jest.advanceTimersByTime(1); | ||
expect(result.current[0]).toBe('Hello world!'); | ||
}); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
import { renderHook } from '@testing-library/react-hooks/server'; | ||
import { useDebouncedState } from '../..'; | ||
|
||
describe('useDebouncedState', () => { | ||
beforeAll(() => { | ||
jest.useFakeTimers(); | ||
}); | ||
|
||
afterAll(() => { | ||
jest.useRealTimers(); | ||
}); | ||
|
||
it('should be defined', () => { | ||
expect(useDebouncedState).toBeDefined(); | ||
}); | ||
|
||
it('should render', () => { | ||
const { result } = renderHook(() => useDebouncedState(undefined, 200)); | ||
expect(result.error).toBeUndefined(); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,20 @@ | ||
import { Dispatch, SetStateAction } from 'react'; | ||
import { useDebouncedCallback, useSafeState } from '..'; | ||
|
||
/** | ||
* Lise `useSafeState` but its state setter is debounced. | ||
* | ||
* @param initialState Initial state to pass to underlying `useSafeState`. | ||
* @param delay Debounce delay. | ||
* @param maxWait Maximum amount of milliseconds that function can be delayed | ||
* before it's force execution. 0 means no max wait. | ||
*/ | ||
export function useDebouncedState<S>( | ||
initialState: S | (() => S), | ||
delay: number, | ||
maxWait = 0 | ||
): [S, Dispatch<SetStateAction<S>>] { | ||
const [state, setState] = useSafeState(initialState); | ||
|
||
return [state, useDebouncedCallback(setState, [], delay, maxWait)]; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters