Skip to content

Commit

Permalink
Merge pull request #180 from xnimorz/isPending
Browse files Browse the repository at this point in the history
Fix isPending state
  • Loading branch information
xnimorz authored Jul 28, 2024
2 parents d4f9cc9 + da112e0 commit 460b1df
Show file tree
Hide file tree
Showing 6 changed files with 104 additions and 8 deletions.
4 changes: 4 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,7 @@
## 10.0.2

- Fixed: `isPending` does not reset the state if the tracked value hasn't changed.. See https://github.com/xnimorz/use-debounce/issues/178

## 10.0.1

- Fixed flush method return args, thanks to [@h](https://github.com/h)
Expand Down
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -68,6 +68,7 @@ https://github.com/xnimorz/use-debounce/blob/master/CHANGELOG.md
## Simple values debouncing

According to https://twitter.com/dan_abramov/status/1060729512227467264
WebArchive link: https://web.archive.org/web/20210828073432/https://twitter.com/dan_abramov/status/1060729512227467264

```javascript
import React, { useState } from 'react';
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "use-debounce",
"version": "10.0.1",
"version": "10.0.2",
"description": "Debounce hook for react",
"source": "src/index.ts",
"main": "dist/index.js",
Expand Down
4 changes: 4 additions & 0 deletions src/useDebounce.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,9 @@ export default function useDebounce<T>(
previousValue.current = value;
}

if (eq(state as T, value)) {
debounced.cancel();
}

return [state as T, debounced];
}
8 changes: 4 additions & 4 deletions src/useDebouncedCallback.ts
Original file line number Diff line number Diff line change
Expand Up @@ -126,9 +126,9 @@ export default function useDebouncedCallback<
// Always keep the latest version of debounce callback, with no wait time.
funcRef.current = func;

const isClientSize = typeof window !== 'undefined';
const isClientSide = typeof window !== 'undefined';
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
const useRAF = !wait && wait !== 0 && isClientSize;
const useRAF = !wait && wait !== 0 && isClientSide;

if (typeof func !== 'function') {
throw new TypeError('Expected a function');
Expand Down Expand Up @@ -229,7 +229,7 @@ export default function useDebouncedCallback<
};

const func: DebouncedState<T> = (...args: Parameters<T>): ReturnType<T> => {
if (!isClientSize && !debounceOnServer) {
if (!isClientSide && !debounceOnServer) {
return;
}
const time = Date.now();
Expand Down Expand Up @@ -290,7 +290,7 @@ export default function useDebouncedCallback<
maxWait,
trailing,
useRAF,
isClientSize,
isClientSide,
debounceOnServer,
]);

Expand Down
93 changes: 90 additions & 3 deletions test/useDebounce.test.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ describe('useDebounce', () => {

// timeout shouldn't have been called yet after leading call was executed
// @ts-ignore
expect(screen.getByRole('test')).toHaveTextContent('Hello world');
expect(screen.getByRole('test')).toHaveTextContent('Hello again');

act(() => {
jest.runAllTimers();
Expand Down Expand Up @@ -283,13 +283,13 @@ describe('useDebounce', () => {

const tree = render(<Component text={'Hello'} />);

expect(eq).toHaveBeenCalledTimes(1);
expect(eq).toHaveBeenCalledTimes(2);

act(() => {
tree.rerender(<Component text="Test" />);
});

expect(eq).toHaveBeenCalledTimes(2);
expect(eq).toHaveBeenCalledTimes(4);
expect(eq).toHaveBeenCalledWith('Hello', 'Test');
// Since the equality function always returns true, expect the value to stay the same
// @ts-ignore
Expand Down Expand Up @@ -414,4 +414,91 @@ describe('useDebounce', () => {
// @ts-ignore
expect(screen.getByRole('test')).toHaveTextContent('Hello world');
});


it('Handles isPending', () => {
function Component({propValue}) {
const [value, fns] = useDebounce(propValue, 1000);
return (
<div>
<div role="value">{value}</div>
<div role="pending">{fns.isPending().toString()}</div>
</div>
);
}

const tree = render(<Component propValue={'Hello'} />);

// check inited value
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('false');

act(() => {
tree.rerender(<Component propValue={'Hello 1'} />);
});
// timeout shouldn't have called yet
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('true');

act(() => {
jest.runAllTimers();
});
// after runAllTimer text should be updated
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello 1');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('false');
})

it('Should handle isPending state correctly while switching between bounced values', () => {
function Component({propValue}) {
const [value, fns] = useDebounce(propValue, 1000);
return (
<div>
<div role="value">{value}</div>
<div role="pending">{fns.isPending().toString()}</div>
</div>
);
}

const tree = render(<Component propValue={'Hello'} />);

// check inited value
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('false');

act(() => {
tree.rerender(<Component propValue={'Hello 1'} />);
});
// timeout shouldn't have called yet
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('true');

act(() => {
tree.rerender(<Component propValue={'Hello'} />);
});

// timeout shouldn't have called yet
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('false');

act(() => {
jest.runAllTimers();
});
// after runAllTimer text should be updated
// @ts-ignore
expect(screen.getByRole('value')).toHaveTextContent('Hello');
// @ts-ignore
expect(screen.getByRole('pending')).toHaveTextContent('false');
})
});

0 comments on commit 460b1df

Please sign in to comment.