Files
sentinel/static/app/views/ddm/utils/useStructuralSharing.tsx
2024-02-15 17:01:50 +00:00

67 lines
1.8 KiB
TypeScript

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<T>(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 const useStructuralSharing = (value: any) => {
const previeousValue = useRef<any>(value);
return useMemo(() => {
const newValue = structuralSharing(previeousValue.current, value);
previeousValue.current = newValue;
return newValue;
}, [value]);
};