From 7cc69cee7a0e81cb57cce95be0869e97b54d59f2 Mon Sep 17 00:00:00 2001 From: Sridhar Bala Date: Sun, 13 Sep 2026 02:43:42 +0530 Subject: [PATCH] fix(useMap): skip rerenders for unchanged values --- src/useMap/index.dom.test.ts | 46 ++++++++++++++++++++++++++++++++++++ src/useMap/index.ts | 9 ++++++- 2 files changed, 54 insertions(+), 1 deletion(-) diff --git a/src/useMap/index.dom.test.ts b/src/useMap/index.dom.test.ts index 831723ef..28ee24ef 100644 --- a/src/useMap/index.dom.test.ts +++ b/src/useMap/index.dom.test.ts @@ -86,4 +86,50 @@ describe('useMap', () => { spy.mockRestore(); }); + + it.each([0, Number.NaN, undefined, {count: 0}])('does not rerender when setting the same value %s', async (value) => { + let renders = 0; + const {result} = await renderHook(() => [++renders, useMap([['key', value]])] as const); + const [, map] = expectResultValue(result); + + await act(async () => { + expect(map.set('key', value)).toBe(map); + }); + + expect(expectResultValue(result)[0]).toBe(1); + expect(map.has('key')).toBe(true); + expect(map.get('key')).toBe(value); + }); + + it.each([ + [0, 1], + [0, -0], + [-0, 0], + [{count: 0}, {count: 0}], + ])('rerenders when changing a value from %s to %s', async (before, after) => { + let renders = 0; + const {result} = await renderHook(() => [++renders, useMap([['key', before]])] as const); + const [, map] = expectResultValue(result); + + await act(async () => { + expect(map.set('key', after)).toBe(map); + }); + + expect(expectResultValue(result)[0]).toBe(2); + expect(map.get('key')).toBe(after); + }); + + it('inserts a missing key with an undefined value and rerenders', async () => { + let renders = 0; + const {result} = await renderHook(() => [++renders, useMap()] as const); + const [, map] = expectResultValue(result); + + await act(async () => { + expect(map.set('key', undefined)).toBe(map); + }); + + expect(expectResultValue(result)[0]).toBe(2); + expect(map.has('key')).toBe(true); + expect(map.size).toBe(1); + }); }); diff --git a/src/useMap/index.ts b/src/useMap/index.ts index 205766fc..fb5eafa8 100644 --- a/src/useMap/index.ts +++ b/src/useMap/index.ts @@ -6,6 +6,9 @@ const proto = Map.prototype; /** * Tracks the state of a `Map`. * + * `set` rerenders when adding a key or changing its value (compared with `Object.is`). + * Replace object values rather than mutating and setting the same reference. + * * @param entries Initial entries iterator for underlying `Map` constructor. */ @@ -19,8 +22,12 @@ export function useMap(entries?: ReadonlyArray { + const [key, value] = args; + const changed = !map.has(key) || !Object.is(map.get(key), value); proto.set.apply(map, args); - rerender(); + if (changed) { + rerender(); + } return map; };