UNPKG

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
"use strict"; 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 });