diff --git a/src/useLocalStorage.test.tsx b/src/useLocalStorage.test.tsx new file mode 100644 index 00000000..4b0a058d --- /dev/null +++ b/src/useLocalStorage.test.tsx @@ -0,0 +1,23 @@ +/* +Copyright 2025 New Vector Ltd. + +SPDX-License-Identifier: AGPL-3.0-only OR LicenseRef-Element-Commercial +Please see LICENSE in the repository root for full details. +*/ + +import { test } from "vitest"; +import { render, screen } from "@testing-library/react"; +import { type FC, useEffect } from "react"; + +import { setLocalStorageItem, useLocalStorage } from "./useLocalStorage"; + +test("useLocalStorage reacts to changes made by an effect mounted on the same render", () => { + localStorage.clear(); + const Test: FC = () => { + useEffect(() => setLocalStorageItem("my-value", "Hello!"), []); + const [myValue] = useLocalStorage("my-value"); + return myValue; + }; + render(); + screen.getByText("Hello!"); +}); diff --git a/src/useLocalStorage.ts b/src/useLocalStorage.ts index 1394e0d3..517c7c62 100644 --- a/src/useLocalStorage.ts +++ b/src/useLocalStorage.ts @@ -8,6 +8,8 @@ Please see LICENSE in the repository root for full details. import EventEmitter from "events"; import { useCallback, useEffect, useState } from "react"; +import { useLatest } from "./useLatest"; + type LocalStorageItem = ReturnType; // Bus to notify other useLocalStorage consumers when an item is changed @@ -20,13 +22,22 @@ export const useLocalStorage = ( const [value, setValue] = useState(() => localStorage.getItem(key), ); + const latestValue = useLatest(value); useEffect(() => { + // We're about to set up the bus listener that will enable us to react to + // any future updates to the localStorage item. However, it's possible that + // we already missed an update if there was an effect which modified the + // item in the time *between* the render phase of useLocalStorage and the + // execution of this effect. Let's update the state if that happened. + const stored = localStorage.getItem(key); + if (latestValue.current !== stored) setValue(stored); + localStorageBus.on(key, setValue); return (): void => { localStorageBus.off(key, setValue); }; - }, [key, setValue]); + }, [key, latestValue, setValue]); return [ value,