Skip to content

Commit 16dc765

Browse files
author
babin
committed
main 🧊 rework use previous
1 parent 151ff64 commit 16dc765

File tree

2 files changed

+59
-8
lines changed

2 files changed

+59
-8
lines changed

‎src/hooks/usePrevious/usePrevious.test.ts‎

Lines changed: 43 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,48 @@ it('Should return previous value after update', () => {
1818
rerender(1);
1919
expect(result.current).toBe(0);
2020

21-
rerender(2);
21+
rerender(Number.NaN);
2222
expect(result.current).toBe(1);
23+
24+
rerender(0);
25+
expect(result.current).toBe(Number.NaN);
26+
27+
rerender(-0);
28+
expect(result.current).toBe(0);
29+
30+
rerender(1);
31+
expect(result.current).toBe(-0);
32+
});
33+
34+
it('Should return previous object value after update', () => {
35+
const { result, rerender } = renderHook((state) => usePrevious(state), {
36+
initialProps: { count: 0 }
37+
});
38+
39+
expect(result.current).toBe(undefined);
40+
41+
rerender({ count: 1 });
42+
expect(result.current).toStrictEqual({ count: 0 });
43+
44+
rerender({ count: 2 });
45+
expect(result.current).toStrictEqual({ count: 1 });
46+
});
47+
48+
it('Should return previous object with custom options.equality', () => {
49+
const equality = (prev: any, next: any) => JSON.stringify(prev) === JSON.stringify(next); // Deep equality check
50+
51+
const { result, rerender } = renderHook((state) => usePrevious(state, { equality }), {
52+
initialProps: { count: 0, data: [1, 2, 3] }
53+
});
54+
55+
expect(result.current).toBe(undefined);
56+
57+
rerender({ count: 0, data: [1, 2, 3] });
58+
expect(result.current).toBe(undefined);
59+
60+
rerender({ count: 1, data: [1, 2, 3] });
61+
expect(result.current).toStrictEqual({ count: 0, data: [1, 2, 3] });
62+
63+
rerender({ count: 2, data: [4, 5, 6] });
64+
expect(result.current).toStrictEqual({ count: 1, data: [1, 2, 3] });
2365
});
Lines changed: 16 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,8 @@
1-
import { useEffect, useRef } from 'react';
1+
import { useRef } from 'react';
2+
3+
export interface UsePreviousOptions<Value> {
4+
equality: (a: Value, b: Value) => boolean;
5+
}
26

37
/**
48
* @name usePrevious
@@ -7,17 +11,22 @@ import { useEffect, useRef } from 'react';
711
*
812
* @template Value The type of the value
913
* @param {Value} value The value to get the previous value
14+
* @param {(a: Value, b: Value) => boolean} [options.equality] The custom equality function to determine if the value has changed
1015
* @returns {Value} The previous value
1116
*
1217
* @example
1318
* const prevValue = usePrevious(value);
1419
*/
15-
export const usePrevious = <Value>(value: Value) => {
16-
const ref = useRef<Value>();
20+
export const usePrevious = <Value>(value: Value, options?: UsePreviousOptions<Value>) => {
21+
const currentRef = useRef<Value>(value);
22+
const previousRef = useRef<Value>();
23+
24+
const equality = options?.equality ?? Object.is;
1725

18-
useEffect(() => {
19-
ref.current = value;
20-
});
26+
if (!equality(value, currentRef.current)) {
27+
previousRef.current = currentRef.current;
28+
currentRef.current = value;
29+
}
2130

22-
return ref.current;
31+
return previousRef.current;
2332
};

0 commit comments

Comments
 (0)