@shined/reactive
Version:
⚛️ Proxy-driven state library for JavaScript application, Intuitive, Flexible and Written in TypeScript.
101 lines (81 loc) • 3.2 kB
text/typescript
const hasMap = typeof Map === 'function'
const hasSet = typeof Set === 'function'
const hasHTMLElementType = typeof HTMLElement !== 'undefined'
const hasArrayBuffer = typeof ArrayBuffer === 'function' && !!ArrayBuffer.isView
/**
* @description `react-fast-compare` rewritten using TypeScript
* @from {@link https://github.com/FormidableLabs/react-fast-compare/blob/6f7d8afe02e4480c32f5af16f571367cccd47abc/index.js | react-fast-compare - GitHub}
*/
export function reactFastCompare(objA: unknown, objB: unknown) {
try {
return equal(objA, objB)
} catch (error: any) {
if ((error.message || '').match(/stack|recursion/i)) {
console.warn('react-fast-compare cannot handle circular refs')
return false
}
throw error
}
}
function equal(objA: any, objB: any) {
if (objA === objB) return true
if (objA && objB && typeof objA === 'object' && typeof objB === 'object') {
if (objA.constructor !== objB.constructor) return false
let length
let i
let keys
if (Array.isArray(objA)) {
length = objA.length
if (length !== objB.length) return false
for (i = length; i-- !== 0; ) if (!equal(objA[i], objB[i])) return false
return true
}
let it
if (hasMap && objA instanceof Map && objB instanceof Map) {
if (objA.size !== objB.size) return false
it = objA.entries()
while (!(i = it.next()).done) if (!objB.has(i.value[0])) return false
it = objA.entries()
while (!(i = it.next()).done) if (!equal(i.value[1], objB.get(i.value[0]))) return false
return true
}
if (hasSet && objA instanceof Set && objB instanceof Set) {
if (objA.size !== objB.size) return false
it = objA.entries()
while (!(i = it.next()).done) if (!objB.has(i.value[0])) return false
return true
}
if (hasArrayBuffer && ArrayBuffer.isView(objA) && ArrayBuffer.isView(objB)) {
length = (objA as any)?.length as never
if (length !== (objB as any)?.length) return false
for (i = length; i-- !== 0; ) if (objA[i] !== objB[i]) return false
return true
}
if (objA.constructor === RegExp) return objA.source === objB.source && objA.flags === objB.flags
if (
objA.valueOf !== Object.prototype.valueOf &&
typeof objA.valueOf === 'function' &&
typeof objB.valueOf === 'function'
)
return objA.valueOf() === objB.valueOf()
if (
objA.toString !== Object.prototype.toString &&
typeof objA.toString === 'function' &&
typeof objB.toString === 'function'
)
return objA.toString() === objB.toString()
keys = Object.keys(objA)
length = keys.length
if (length !== Object.keys(objB).length) return false
for (i = length; i-- !== 0; ) if (!Object.prototype.hasOwnProperty.call(objB, keys[i])) return false
if (hasHTMLElementType && objA instanceof HTMLElement) return false
for (i = length; i-- !== 0; ) {
if ((keys[i] === '_owner' || keys[i] === '__v' || keys[i] === '__o') && objA.$$typeof) {
continue
}
if (!equal(objA[keys[i]], objB[keys[i]])) return false
}
return true
}
return objA !== objA && objB !== objB
}