Skip to content

Instantly share code, notes, and snippets.

@mustafadalga
Created November 18, 2023 09:24
Show Gist options
  • Save mustafadalga/f148f987e5f6a8de1a332e49915cfda2 to your computer and use it in GitHub Desktop.
Save mustafadalga/f148f987e5f6a8de1a332e49915cfda2 to your computer and use it in GitHub Desktop.
Unit Tests for useDeepCompareMemoize: Testing Deep Comparison React Hook with Vitest and Testing Library
import { describe, it, expect } from "vitest";
import { renderHook } from "@testing-library/react";
import useDeepCompareMemoize from "./useDeepCompareMemoize";
describe("useDeepCompareMemoize", () => {
it('should return a memoized version of the input value based on deep comparison', () => {
const initialData = { user: { name: 'John', age: 25, city: 'New York' } };
const newData = { user: { name: 'Alice', age: 30, city: 'San Francisco' } };
const identicalData = { user: { name: 'John', age: 25, city: 'New York' } };
const { result, rerender } = renderHook(
({ data }) => useDeepCompareMemoize(data),
{ initialProps: { data: initialData } },
);
expect(result.current).toEqual(initialData);
// Re-render with new data, expect result to change
rerender({ data: newData });
expect(result.current).toEqual(newData);
// Re-render with identical data, expect result not to change
rerender({ data: identicalData });
expect(result.current).toEqual(initialData);
});
it('should handle primitive values', () => {
const initialNumber = 2023;
const newNumber = 2024;
const identicalNumber = 2023;
const { result, rerender } = renderHook(
({ data }) => useDeepCompareMemoize(data),
{ initialProps: { data: initialNumber } }
);
expect(result.current).toEqual(initialNumber);
// Re-render with the same value, expect result not to change
rerender({ data: newNumber });
expect(result.current).toEqual(newNumber);
// Re-render with a new value, expect result to change
rerender({ data: identicalNumber });
expect(result.current).toEqual(initialNumber);
});
it('should handle arrays', () => {
const initialArray = [ 'apple', 'orange', 'banana' ];
const newArray = [ 'grape', 'kiwi', 'pineapple' ];
const identicalArray = [ 'apple', 'orange', 'banana' ];
const { result, rerender } = renderHook(
({ data }) => useDeepCompareMemoize(data),
{ initialProps: { data: initialArray } }
);
expect(result.current).toEqual(initialArray);
// Re-render with a new array, expect result to change
rerender({ data: newArray });
expect(result.current).toEqual(newArray);
// Re-render with an identical array, expect result not to change
rerender({ data: identicalArray });
expect(result.current).toEqual(initialArray);
});
})
import { useRef } from 'react';
import { isEqual, cloneDeep } from 'lodash';
/**
* Custom React hook for deep comparison memoization.
*
* @remarks
* Traditional useMemo doesn't detect changes in nested data structures.
* This hook uses lodash's `isEqual` for deep comparison and returns a memoized version of the provided value.
* If the current value is different from the previous one (in a deep comparison sense), it clones and returns the new value.
*
* This hook can be particularly useful when combined with React's `useMemo` or `useCallback` to avoid unnecessary re-renders or recalculations when dealing with deep nested data structures.
*
* @param value - The value to be deeply compared and possibly memoized.
* @returns A memoized version of the input value based on deep comparison.
*
* @example
* ```tsx
* const data = { a: { b: { c: 1 } } };
*
* const memoizedData = useDeepCompareMemoize(data);
* ```
*
* @see {@link https://sft.hashnode.dev/solving-the-nested-object-change-detection-issue-with-usememo-in-react} for the original resource and explanation.
*/
const useDeepCompareMemoize = <T>(value: T): T => {
const ref = useRef<T>();
if (!isEqual(value, ref.current)) {
ref.current = cloneDeep(value);
}
return ref.current!;
};
export default useDeepCompareMemoize;
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment