diff --git a/.changeset/stale-poets-walk.md b/.changeset/stale-poets-walk.md new file mode 100644 index 0000000000..41afa2a274 --- /dev/null +++ b/.changeset/stale-poets-walk.md @@ -0,0 +1,5 @@ +--- +"rooks": patch +--- + +useLocalstorageState: ensure stable identity for set/remove functions diff --git a/packages/rooks/src/__tests__/useLocalstorageState.spec.tsx b/packages/rooks/src/__tests__/useLocalstorageState.spec.tsx index 94373c3e2c..b5f45be7d9 100644 --- a/packages/rooks/src/__tests__/useLocalstorageState.spec.tsx +++ b/packages/rooks/src/__tests__/useLocalstorageState.spec.tsx @@ -68,6 +68,20 @@ describe("useLocalstorageState basic", () => { const removeAfterRerender = result.current[2]; expect(setBeforeRerender).toBe(setAfterRerender); expect(removeBeforeRerender).toBe(removeAfterRerender); + act(() => { + setBeforeRerender("next value"); + }); + const setAfterSet = result.current[1]; + const removeAfterSet = result.current[2]; + expect(setBeforeRerender).toBe(setAfterSet); + expect(removeBeforeRerender).toBe(removeAfterSet); + act(() => { + removeBeforeRerender(); + }); + const setAfterRemove = result.current[1]; + const removeAfterRemove = result.current[2]; + expect(setBeforeRerender).toBe(setAfterRemove); + expect(removeBeforeRerender).toBe(removeAfterRemove); }); it("initializes correctly", () => { diff --git a/packages/rooks/src/hooks/useLocalstorageState.ts b/packages/rooks/src/hooks/useLocalstorageState.ts index d69ac36a00..7f63ddab3e 100644 --- a/packages/rooks/src/hooks/useLocalstorageState.ts +++ b/packages/rooks/src/hooks/useLocalstorageState.ts @@ -1,5 +1,6 @@ import type { Dispatch, SetStateAction } from "react"; import { useMemo, useState, useEffect, useCallback, useRef } from "react"; +import { useFreshRef } from "./useFreshRef"; // Gets value from localstorage function getValueFromLocalStorage(key: string) { @@ -174,18 +175,20 @@ function useLocalstorageState( [customEventTypeName] ); + const currentValue = useFreshRef(value, true); + const set = useCallback( (newValue: SetStateAction) => { const resolvedNewValue = typeof newValue === "function" - ? (newValue as (prevState: S) => S)(value) + ? (newValue as (prevState: S) => S)(currentValue.current) : newValue; isUpdateFromCrossDocumentListener.current = false; isUpdateFromWithinDocumentListener.current = false; setValue(resolvedNewValue); broadcastValueWithinDocument(resolvedNewValue); }, - [broadcastValueWithinDocument, value] + [broadcastValueWithinDocument, currentValue] ); const remove = useCallback(() => {