Skip to content

Commit

Permalink
feat: add persistence with local-storage for sidebar open state
Browse files Browse the repository at this point in the history
  • Loading branch information
SeanCassiere committed Apr 18, 2023
1 parent a1ec4d9 commit ffef2df
Show file tree
Hide file tree
Showing 2 changed files with 93 additions and 1 deletion.
6 changes: 5 additions & 1 deletion packages/trpc-panel/src/react-app/Root.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import {
HeadersContextProvider,
useHeaders,
} from "@src/react-app/components/contexts/HeadersContext";
import { useLocalStorage } from "@src/react-app/components/hooks/useLocalStorage";
import { HeadersPopup } from "@src/react-app/components/HeadersPopup";
import { Toaster } from "react-hot-toast";
import { SiteNavigationContextProvider } from "@src/react-app/components/contexts/SiteNavigationContext";
Expand Down Expand Up @@ -80,7 +81,10 @@ function ClientProviders({
}

function AppInnards({ rootRouter }: { rootRouter: ParsedRouter }) {
const [sidebarOpen, setSidebarOpen] = useState(true);
const [sidebarOpen, setSidebarOpen] = useLocalStorage(
"trpc-panel.show-minimap",
true
);

return (
<div className="flex flex-col flex-1 relative">
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
// a stripped down version of https://usehooks-ts.com/react-hook/use-local-storage
import {
useCallback,
useEffect,
useState,
type Dispatch,
type SetStateAction,
} from "react";

type SetValue<T> = Dispatch<SetStateAction<T>>;

export function useLocalStorage<T>(
key: string,
initialValue: T
): [T, SetValue<T>] {
// Get from local storage then
// parse stored json or return initialValue
const readValue = useCallback((): T => {
// Prevent build error "window is undefined" but keeps working
if (typeof window === "undefined") {
return initialValue;
}

try {
const item = window.localStorage.getItem(key);
return item ? (parseJSON(item) as T) : initialValue;
} catch (error) {
console.warn(
`tRPC-Panel.useLocalStorage: Error reading localStorage key “${key}”:`,
error
);
return initialValue;
}
}, [initialValue, key]);

// State to store our value
// Pass initial state function to useState so logic is only executed once
const [storedValue, setStoredValue] = useState<T>(readValue);

// Return a wrapped version of useState's setter function that ...
// ... persists the new value to localStorage.
const setValue: SetValue<T> = useCallback(
(value) => {
// Prevent build error "window is undefined" but keeps working
if (typeof window === "undefined") {
console.warn(
`tRPC-Panel.useLocalStorage: Tried setting localStorage key “${key}” even though environment is not a client`
);
}

try {
// Allow value to be a function so we have the same API as useState
const newValue = value instanceof Function ? value(storedValue) : value;

// Save to local storage
window.localStorage.setItem(key, JSON.stringify(newValue));

// Save state
setStoredValue(newValue);
} catch (error) {
console.warn(
`tRPC-Panel.useLocalStorage: Error setting localStorage key “${key}”:`,
error
);
}
},
[storedValue]
);

useEffect(() => {
setStoredValue(readValue());
// eslint-disable-next-line react-hooks/exhaustive-deps
}, []);

return [storedValue, setValue];
}

// A wrapper for "JSON.parse()"" to support "undefined" value
function parseJSON<T>(value: string | null): T | undefined {
try {
return value === "undefined" ? undefined : JSON.parse(value ?? "");
} catch {
console.log("tRPC-Panel.useLocalStorage.parseJSON: parsing error on", {
value,
});
return undefined;
}
}

0 comments on commit ffef2df

Please sign in to comment.