From 4ac0950e710fab012d5933a1a4de2984acac08b4 Mon Sep 17 00:00:00 2001 From: Daishi Kato Date: Sat, 23 Mar 2024 13:01:10 +0900 Subject: [PATCH] [v5] Rewrite shallow to support iterables (#2427) --- src/vanilla/shallow.ts | 56 +++++++++++++++++++++++----------- tests/vanilla/shallow.test.tsx | 6 ++++ 2 files changed, 45 insertions(+), 17 deletions(-) diff --git a/src/vanilla/shallow.ts b/src/vanilla/shallow.ts index 6716eed5ac..84b1c42d2f 100644 --- a/src/vanilla/shallow.ts +++ b/src/vanilla/shallow.ts @@ -1,4 +1,22 @@ -export function shallow(objA: T, objB: T) { +const isIterable = (obj: object): obj is Iterable => + Symbol.iterator in obj + +const compareMapLike = ( + iterableA: Iterable<[unknown, unknown]>, + iterableB: Iterable<[unknown, unknown]>, +) => { + const mapA = iterableA instanceof Map ? iterableA : new Map(iterableA) + const mapB = iterableB instanceof Map ? iterableB : new Map(iterableB) + if (mapA.size !== mapB.size) return false + for (const [key, value] of mapA) { + if (!Object.is(value, mapB.get(key))) { + return false + } + } + return true +} + +export function shallow(objA: T, objB: T): boolean { if (Object.is(objA, objB)) { return true } @@ -11,26 +29,30 @@ export function shallow(objA: T, objB: T) { return false } - if (objA instanceof Map && objB instanceof Map) { - if (objA.size !== objB.size) return false - - for (const [key, value] of objA) { - if (!Object.is(value, objB.get(key))) { - return false - } + if (isIterable(objA) && isIterable(objB)) { + const iteratorA = objA[Symbol.iterator]() + const iteratorB = objB[Symbol.iterator]() + let nextA = iteratorA.next() + let nextB = iteratorB.next() + if ( + Array.isArray(nextA.value) && + Array.isArray(nextB.value) && + nextA.value.length === 2 && + nextB.value.length === 2 + ) { + return compareMapLike( + objA as Iterable<[unknown, unknown]>, + objB as Iterable<[unknown, unknown]>, + ) } - return true - } - - if (objA instanceof Set && objB instanceof Set) { - if (objA.size !== objB.size) return false - - for (const value of objA) { - if (!objB.has(value)) { + while (!nextA.done && !nextB.done) { + if (!Object.is(nextA.value, nextB.value)) { return false } + nextA = iteratorA.next() + nextB = iteratorB.next() } - return true + return !!nextA.done && !!nextB.done } const keysA = Object.keys(objA) diff --git a/tests/vanilla/shallow.test.tsx b/tests/vanilla/shallow.test.tsx index 7587375ae7..07f0561ed5 100644 --- a/tests/vanilla/shallow.test.tsx +++ b/tests/vanilla/shallow.test.tsx @@ -91,6 +91,12 @@ describe('shallow', () => { expect(shallow(firstFnCompare, secondFnCompare)).toBe(false) }) + + it('compares URLSearchParams', () => { + const a = new URLSearchParams({ hello: 'world' }) + const b = new URLSearchParams({ zustand: 'shallow' }) + expect(shallow(a, b)).toBe(false) + }) }) describe('unsupported cases', () => {