react-deep-check
Version:
Lightweight deep equality utilities and React hooks (useDeepCompareEffect, memo) for reliable deep checks in React.
225 lines (220 loc) • 6.89 kB
JavaScript
;
var __defProp = Object.defineProperty;
var __getOwnPropDesc = Object.getOwnPropertyDescriptor;
var __getOwnPropNames = Object.getOwnPropertyNames;
var __hasOwnProp = Object.prototype.hasOwnProperty;
var __export = (target, all) => {
for (var name in all)
__defProp(target, name, { get: all[name], enumerable: true });
};
var __copyProps = (to, from, except, desc) => {
if (from && typeof from === "object" || typeof from === "function") {
for (let key of __getOwnPropNames(from))
if (!__hasOwnProp.call(to, key) && key !== except)
__defProp(to, key, { get: () => from[key], enumerable: !(desc = __getOwnPropDesc(from, key)) || desc.enumerable });
}
return to;
};
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
// src/index.ts
var index_exports = {};
__export(index_exports, {
deepEqual: () => deepEqual,
stableHash: () => stableHash,
useDeepCompareEffect: () => useDeepCompareEffect,
useDeepCompareMemoize: () => useDeepCompareMemoize,
useShallowStable: () => useShallowStable,
useStableHash: () => useStableHash
});
module.exports = __toCommonJS(index_exports);
// src/deepEqual.ts
var hasOwn = Object.prototype.hasOwnProperty;
function deepEqual(a, b, options = {}, _stackA = [], _stackB = [], _path = []) {
if (a === b) {
return a !== 0 || 1 / a === 1 / b;
}
if (a !== a && b !== b) return true;
if (a == null || b == null) return false;
const typeA = typeof a;
const typeB = typeof b;
if (typeA !== "object" && typeB !== "object") return false;
let idx = _stackA.indexOf(a);
if (idx !== -1) {
return _stackB[idx] === b;
}
_stackA.push(a);
_stackB.push(b);
if (options.compare) {
const res = options.compare(a, b, _path);
if (res !== void 0) return res;
}
if (Array.isArray(a)) {
if (!Array.isArray(b) || a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
_path.push(String(i));
if (!deepEqual(a[i], b[i], options, _stackA, _stackB, _path)) return false;
_path.pop();
}
_stackA.pop();
_stackB.pop();
return true;
}
const tagA = Object.prototype.toString.call(a);
const tagB = Object.prototype.toString.call(b);
if (tagA !== tagB) return false;
switch (tagA) {
case "[object Date]":
_stackA.pop();
_stackB.pop();
return a.getTime() === b.getTime();
case "[object RegExp]":
_stackA.pop();
_stackB.pop();
return a.source === b.source && a.flags === b.flags;
case "[object Map]": {
if (a.size !== b.size) return false;
for (const [keyA, valA] of a) {
if (!b.has(keyA)) return false;
_path.push(`map:${String(keyA)}`);
if (!deepEqual(valA, b.get(keyA), options, _stackA, _stackB, _path)) return false;
_path.pop();
}
_stackA.pop();
_stackB.pop();
return true;
}
case "[object Set]": {
if (a.size !== b.size) return false;
const arrA = Array.from(a);
const arrB = Array.from(b);
outer: for (const valA of arrA) {
for (let i = 0; i < arrB.length; i++) {
if (deepEqual(valA, arrB[i], options, _stackA, _stackB, _path)) continue outer;
}
return false;
}
_stackA.pop();
_stackB.pop();
return true;
}
default:
break;
}
if (ArrayBuffer.isView(a)) {
if (!ArrayBuffer.isView(b) || a.constructor !== b.constructor || a.length !== b.length) return false;
for (let i = 0; i < a.length; i++) {
if (a[i] !== b[i]) return false;
}
_stackA.pop();
_stackB.pop();
return true;
}
const keysA = Object.keys(a);
const keysB = Object.keys(b);
if (keysA.length !== keysB.length) return false;
for (let i = 0; i < keysA.length; i++) {
if (!hasOwn.call(b, keysA[i])) return false;
}
for (let i = 0; i < keysA.length; i++) {
const key = keysA[i];
_path.push(key);
if (!deepEqual(a[key], b[key], options, _stackA, _stackB, _path)) return false;
_path.pop();
}
_stackA.pop();
_stackB.pop();
return true;
}
// src/stableHash.ts
function stableHash(value, seen = [], path = []) {
const type = typeof value;
switch (type) {
case "undefined":
return "u";
case "boolean":
return value ? "b1" : "b0";
case "number":
return `n${Number.isNaN(value) ? "NaN" : value}`;
case "bigint":
return `bi${value.toString()}`;
case "string":
return `s${value.length}:${value}`;
case "symbol":
return `sym${String(value.description)}`;
case "function":
return `f${value.name}`;
default:
break;
}
if (value === null) return "null";
const idx = seen.indexOf(value);
if (idx !== -1) return `@cycle${idx}`;
seen.push(value);
if (Array.isArray(value)) {
return "A[" + value.map((v) => stableHash(v, seen, path)).join(",") + "]";
}
if (value instanceof Date) return `D${value.getTime()}`;
if (value instanceof RegExp) return `R${value.source}/${value.flags}`;
if (value instanceof Map) {
const parts = [];
value.forEach((v, k) => {
parts.push(`(${stableHash(k, seen, path)}=>${stableHash(v, seen, path)})`);
});
return "M" + parts.join("");
}
if (value instanceof Set) {
const parts = Array.from(value).map((v) => stableHash(v, seen, path)).sort((a, b) => a.localeCompare(b));
return "S" + parts.join("");
}
if (ArrayBuffer.isView(value)) {
return `${value.constructor.name}[${Array.prototype.join.call(value, ",")}]`;
}
const keys = Object.keys(value).sort((a, b) => a.localeCompare(b));
const inner = keys.map((k) => `${k}:${stableHash(value[k], seen, path)}`).join("|");
return `{${inner}}`;
}
// src/hooks.ts
var import_react = require("react");
function useDeepCompareMemoize(value) {
const ref = (0, import_react.useRef)(value);
if (!deepEqual(value, ref.current)) {
ref.current = value;
}
return ref.current;
}
function useDeepCompareEffect(effect, deps) {
const memoDeps = useDeepCompareMemoize(deps);
(0, import_react.useEffect)(effect, memoDeps);
}
function useShallowStable(obj) {
const ref = (0, import_react.useRef)(obj);
const prev = ref.current;
let changed = false;
const keys = Object.keys(obj);
const prevKeys = Object.keys(prev);
if (keys.length !== prevKeys.length) {
changed = true;
} else {
for (const k of keys) {
if (prev[k] !== obj[k]) {
changed = true;
break;
}
}
}
if (changed) ref.current = obj;
return ref.current;
}
function useStableHash(value) {
const memo = useDeepCompareMemoize(value);
return (0, import_react.useMemo)(() => stableHash(memo), [memo]);
}
// Annotate the CommonJS export names for ESM import in node:
0 && (module.exports = {
deepEqual,
stableHash,
useDeepCompareEffect,
useDeepCompareMemoize,
useShallowStable,
useStableHash
});