Skip to content

Commit da112e0

Browse files
committed
Fix isPending state
1 parent d4f9cc9 commit da112e0

File tree

6 files changed

+104
-8
lines changed

6 files changed

+104
-8
lines changed

CHANGELOG.md

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,7 @@
1+
## 10.0.2
2+
3+
- Fixed: `isPending` does not reset the state if the tracked value hasn't changed.. See https://github.com/xnimorz/use-debounce/issues/178
4+
15
## 10.0.1
26

37
- Fixed flush method return args, thanks to [@h](https://github.com/h)

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ https://github.com/xnimorz/use-debounce/blob/master/CHANGELOG.md
6868
## Simple values debouncing
6969

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

7273
```javascript
7374
import React, { useState } from 'react';

package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "use-debounce",
3-
"version": "10.0.1",
3+
"version": "10.0.2",
44
"description": "Debounce hook for react",
55
"source": "src/index.ts",
66
"main": "dist/index.js",

src/useDebounce.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,5 +34,9 @@ export default function useDebounce<T>(
3434
previousValue.current = value;
3535
}
3636

37+
if (eq(state as T, value)) {
38+
debounced.cancel();
39+
}
40+
3741
return [state as T, debounced];
3842
}

src/useDebouncedCallback.ts

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -126,9 +126,9 @@ export default function useDebouncedCallback<
126126
// Always keep the latest version of debounce callback, with no wait time.
127127
funcRef.current = func;
128128

129-
const isClientSize = typeof window !== 'undefined';
129+
const isClientSide = typeof window !== 'undefined';
130130
// Bypass `requestAnimationFrame` by explicitly setting `wait=0`.
131-
const useRAF = !wait && wait !== 0 && isClientSize;
131+
const useRAF = !wait && wait !== 0 && isClientSide;
132132

133133
if (typeof func !== 'function') {
134134
throw new TypeError('Expected a function');
@@ -229,7 +229,7 @@ export default function useDebouncedCallback<
229229
};
230230

231231
const func: DebouncedState<T> = (...args: Parameters<T>): ReturnType<T> => {
232-
if (!isClientSize && !debounceOnServer) {
232+
if (!isClientSide && !debounceOnServer) {
233233
return;
234234
}
235235
const time = Date.now();
@@ -290,7 +290,7 @@ export default function useDebouncedCallback<
290290
maxWait,
291291
trailing,
292292
useRAF,
293-
isClientSize,
293+
isClientSide,
294294
debounceOnServer,
295295
]);
296296

test/useDebounce.test.tsx

Lines changed: 90 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -65,7 +65,7 @@ describe('useDebounce', () => {
6565

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

7070
act(() => {
7171
jest.runAllTimers();
@@ -283,13 +283,13 @@ describe('useDebounce', () => {
283283

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

286-
expect(eq).toHaveBeenCalledTimes(1);
286+
expect(eq).toHaveBeenCalledTimes(2);
287287

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

292-
expect(eq).toHaveBeenCalledTimes(2);
292+
expect(eq).toHaveBeenCalledTimes(4);
293293
expect(eq).toHaveBeenCalledWith('Hello', 'Test');
294294
// Since the equality function always returns true, expect the value to stay the same
295295
// @ts-ignore
@@ -414,4 +414,91 @@ describe('useDebounce', () => {
414414
// @ts-ignore
415415
expect(screen.getByRole('test')).toHaveTextContent('Hello world');
416416
});
417+
418+
419+
it('Handles isPending', () => {
420+
function Component({propValue}) {
421+
const [value, fns] = useDebounce(propValue, 1000);
422+
return (
423+
<div>
424+
<div role="value">{value}</div>
425+
<div role="pending">{fns.isPending().toString()}</div>
426+
</div>
427+
);
428+
}
429+
430+
const tree = render(<Component propValue={'Hello'} />);
431+
432+
// check inited value
433+
// @ts-ignore
434+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
435+
// @ts-ignore
436+
expect(screen.getByRole('pending')).toHaveTextContent('false');
437+
438+
act(() => {
439+
tree.rerender(<Component propValue={'Hello 1'} />);
440+
});
441+
// timeout shouldn't have called yet
442+
// @ts-ignore
443+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
444+
// @ts-ignore
445+
expect(screen.getByRole('pending')).toHaveTextContent('true');
446+
447+
act(() => {
448+
jest.runAllTimers();
449+
});
450+
// after runAllTimer text should be updated
451+
// @ts-ignore
452+
expect(screen.getByRole('value')).toHaveTextContent('Hello 1');
453+
// @ts-ignore
454+
expect(screen.getByRole('pending')).toHaveTextContent('false');
455+
})
456+
457+
it('Should handle isPending state correctly while switching between bounced values', () => {
458+
function Component({propValue}) {
459+
const [value, fns] = useDebounce(propValue, 1000);
460+
return (
461+
<div>
462+
<div role="value">{value}</div>
463+
<div role="pending">{fns.isPending().toString()}</div>
464+
</div>
465+
);
466+
}
467+
468+
const tree = render(<Component propValue={'Hello'} />);
469+
470+
// check inited value
471+
// @ts-ignore
472+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
473+
// @ts-ignore
474+
expect(screen.getByRole('pending')).toHaveTextContent('false');
475+
476+
act(() => {
477+
tree.rerender(<Component propValue={'Hello 1'} />);
478+
});
479+
// timeout shouldn't have called yet
480+
// @ts-ignore
481+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
482+
// @ts-ignore
483+
expect(screen.getByRole('pending')).toHaveTextContent('true');
484+
485+
act(() => {
486+
tree.rerender(<Component propValue={'Hello'} />);
487+
});
488+
489+
// timeout shouldn't have called yet
490+
// @ts-ignore
491+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
492+
// @ts-ignore
493+
expect(screen.getByRole('pending')).toHaveTextContent('false');
494+
495+
act(() => {
496+
jest.runAllTimers();
497+
});
498+
// after runAllTimer text should be updated
499+
// @ts-ignore
500+
expect(screen.getByRole('value')).toHaveTextContent('Hello');
501+
// @ts-ignore
502+
expect(screen.getByRole('pending')).toHaveTextContent('false');
503+
})
417504
});

0 commit comments

Comments
 (0)