import {useMemo, useRef} from 'react'; /** * Check if two objects have the same keys */ const checkSameKeys = (obj1: any, obj2: any) => { const keys1 = new Set(Object.keys(obj1)); const keys2 = new Set(Object.keys(obj2)); if (keys1.size !== keys2.size) { return false; } for (const key in keys1) { if (!keys2.has(key)) { return false; } } return true; }; /** * Merge oldVlaue and newValue while trying to preserve references of unchanged objects / arrays */ export function structuralSharing(oldValue: T, newValue: T): T { if (oldValue === newValue) { return oldValue; } if (Array.isArray(oldValue) && Array.isArray(newValue)) { let hasChanges = oldValue.length !== newValue.length; const newArray = newValue.map((item, index) => { const newItem = structuralSharing(oldValue[index], item); if (newItem !== oldValue[index]) { hasChanges = true; } return newItem; }); return hasChanges ? (newArray as any) : oldValue; } if (oldValue === null || newValue === null) { return newValue; } if (typeof oldValue === 'object' && typeof newValue === 'object') { let hasChanges = !checkSameKeys(oldValue, newValue); const newObj = Object.keys(newValue).reduce((acc, key) => { acc[key] = structuralSharing(oldValue[key], newValue[key]); if (acc[key] !== oldValue[key]) { hasChanges = true; } return acc; }, {}); return hasChanges ? (newObj as any) : oldValue; } return newValue; } export function useStructuralSharing(value: T): T { const previousValue = useRef(value); return useMemo(() => { const newValue = structuralSharing(previousValue.current, value); previousValue.current = newValue; return newValue; }, [value]); }