diff --git a/src/index.ts b/src/index.ts index 785fb4c..face65b 100755 --- a/src/index.ts +++ b/src/index.ts @@ -1,4 +1,4 @@ -import { useState } from "react"; +import { useState, useCallback, useRef } from "react"; import { SUPPORTED } from "./utils"; @@ -22,21 +22,29 @@ const useLocalState = ( } }); - const setLocalStateValue = (newValue: SetStateAction) => { - const isCallable = (value: unknown): value is (prevState: S) => S => - typeof value === "function"; - const toStore = isCallable(newValue) ? newValue(value) : newValue; - if (SUPPORTED) window.localStorage.setItem(key, JSON.stringify(toStore)); - setValue(toStore); - }; + const lastValue = useRef(value); + lastValue.current = value; + + const setLocalStateValue = useCallback( + (newValue: SetStateAction) => { + const isCallable = (value: unknown): value is (prevState: S) => S => + typeof value === "function"; + const toStore = isCallable(newValue) + ? newValue(lastValue.current) + : newValue; + if (SUPPORTED) window.localStorage.setItem(key, JSON.stringify(toStore)); + setValue(toStore); + }, + [key] + ); - const reset = () => { + const reset = useCallback(() => { const isCallable = (value: unknown): value is (prevState: S) => S => typeof value === "function"; const toStore = isCallable(defaultValue) ? defaultValue() : defaultValue; setValue(toStore); if (SUPPORTED) window.localStorage.removeItem(key); - }; + }, [defaultValue, key]); return [value, setLocalStateValue, reset]; }; diff --git a/test/index.test.tsx b/test/index.test.tsx index e22f1ef..d7e3eb7 100755 --- a/test/index.test.tsx +++ b/test/index.test.tsx @@ -104,6 +104,48 @@ describe("useLocalState()", () => { expect(values).toEqual(newValue); }); + it("can update value as function", async () => { + const key = "key"; + const value = "something"; + const { result } = renderHook(() => useLocalState(key, value)); + + const [initialValue] = result.current; + expect(initialValue).toEqual(value); + + const newValue = " else"; + act(() => { + const setValue = result.current[1]; + setValue((v) => v + newValue); + }); + + const [values] = result.current; + expect(values).toEqual("something else"); + }); + + it("can update value as function multiple times", async () => { + const key = "key"; + const value = "something"; + const { result } = renderHook(() => useLocalState(key, value)); + + const [initialValue] = result.current; + expect(initialValue).toEqual(value); + + const newValue = " else"; + act(() => { + const setValue = result.current[1]; + setValue((v) => v + newValue); + }); + + const newValue2 = " again"; + act(() => { + const setValue = result.current[1]; + setValue((v) => v + newValue2); + }); + + const [values] = result.current; + expect(values).toEqual("something else again"); + }); + it("can update value as object", async () => { const key = "key"; const value = { something: "something" }; @@ -287,4 +329,40 @@ describe("useLocalState()", () => { expect(resetValues).toEqual(values); expect(localStorage.getItem(key)).toEqual(null); }); + + it("does not change the setter when value is updated", async () => { + const key = "key"; + const value = "something"; + const { result } = renderHook(() => useLocalState(key, value)); + + const setterOne = result.current[1]; + + const newValue = "something else"; + act(() => { + const setValue = result.current[1]; + setValue(newValue); + }); + + const setterTwo = result.current[1]; + + expect(setterOne).toEqual(setterTwo); + }); + + it("does not change the setter when value is updated via function", async () => { + const key = "key"; + const value = "something"; + const { result } = renderHook(() => useLocalState(key, value)); + + const setterOne = result.current[1]; + + const newValue = " else"; + act(() => { + const setValue = result.current[1]; + setValue((v) => v + newValue); + }); + + const setterTwo = result.current[1]; + + expect(setterOne).toEqual(setterTwo); + }); });