Skip to content

Commit

Permalink
[UI v2] feat: Adds useLocalStorage hook (#16900)
Browse files Browse the repository at this point in the history
  • Loading branch information
devinvillarosa authored Jan 30, 2025
1 parent c4ba671 commit a2362a2
Show file tree
Hide file tree
Showing 3 changed files with 79 additions and 0 deletions.
21 changes: 21 additions & 0 deletions ui-v2/src/hooks/use-local-storage.test.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
/* eslint-disable @typescript-eslint/unbound-method */
import { act, renderHook } from "@testing-library/react";
import { describe, expect, it } from "vitest";
import { useLocalStorage } from "./use-local-storage";

describe("useLocalStorage ", () => {
it("is able to synchronize state with local storage", () => {
// TEST
const { result } = renderHook(() => useLocalStorage("name", ""));
const [, setState] = result.current;

expect(localStorage.getItem).toBeCalledWith("name");

act(() => setState("new value"));
const [nextState] = result.current;

// nb: appends a set of "" when storing in local storage
expect(nextState).toEqual("new value");
expect(localStorage.setItem).toBeCalledWith("name", '"new value"');
});
});
47 changes: 47 additions & 0 deletions ui-v2/src/hooks/use-local-storage.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
import { useEffect, useState } from "react";

/**
*
* @param key local storage key
* @param initialValue initial value of stored state
* @returns a hook to synchronize state with local storage
*
* @example
* ```ts
* function MyComponent() {
* const [name, setName] = useLocalStorage<string>('name', '');
* return (
* <div>
* <input type="text" value={name} onChange={e => setName(e.target.value)} />
* <p>Hello, {name}!</p>
* </div>
* );
* }
* ```
*/
export function useLocalStorage<T>(
key: string,
initialValue: T,
): [T, (value: T | ((prevValue: T) => T)) => void] {
const [storedValue, setStoredValue] = useState<T>(() => {
if (typeof window !== "undefined") {
try {
const item = localStorage.getItem(key);
return item ? (JSON.parse(item) as T) : initialValue;
} catch (error) {
console.log(error);
return initialValue;
}
} else {
return initialValue;
}
});

useEffect(() => {
if (typeof window !== "undefined") {
localStorage.setItem(key, JSON.stringify(storedValue));
}
}, [key, storedValue]);

return [storedValue, setStoredValue];
}
11 changes: 11 additions & 0 deletions ui-v2/tests/setup.ts
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,17 @@ class ResizeObserverMock {

global.ResizeObserver = ResizeObserverMock;

// Mock localStorage
const localStorageMock: Storage = {
key: vi.fn(),
length: 0,
getItem: vi.fn(),
setItem: vi.fn(),
removeItem: vi.fn(),
clear: vi.fn(),
};
global.localStorage = localStorageMock;

// Add this along with the ResizeObserver mock
Element.prototype.getBoundingClientRect = vi.fn(() => ({
width: 500,
Expand Down

0 comments on commit a2362a2

Please sign in to comment.