mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-07-03 17:46:39 -04:00
even more useLocalStorage
fixes
This commit is contained in:
@ -1,32 +1,37 @@
|
||||
// Modified from https://github.com/streamich/react-use/blob/e53ca94a0b1f20270b0f75dc2ca1fecf1e119dde/src/useLocalStorage.ts
|
||||
|
||||
import { useCallback, useState, useRef, useLayoutEffect } from "react";
|
||||
import { useCallback, useState, useRef } from "react";
|
||||
import type { Dispatch, SetStateAction } from "react";
|
||||
|
||||
// eslint-disable-next-line @typescript-eslint/no-empty-function
|
||||
const noop = () => {};
|
||||
|
||||
const useLocalStorage = <T>(
|
||||
const useLocalStorage = <T = string>(
|
||||
key: string,
|
||||
initialValue?: T
|
||||
): [T | undefined, Dispatch<SetStateAction<T | undefined>>, typeof noop] => {
|
||||
): [T | undefined, Dispatch<SetStateAction<T | undefined>>, () => void] => {
|
||||
if (typeof window === "undefined" || typeof window.Storage === "undefined") {
|
||||
// immediately return a "dummy" hook instead of throwing an error if localStorage isn't available, either in the
|
||||
// browser or because this hook is being called server-side.
|
||||
return [initialValue as T, noop, noop];
|
||||
}
|
||||
|
||||
// TODO: make these customizable:
|
||||
const serializer = String;
|
||||
const deserializer = (value: any) => value; // eslint-disable-line @typescript-eslint/no-explicit-any
|
||||
// TODO: make these customizable (e.g. `JSON.stringify()` and `JSON.parse()`)
|
||||
const serializer = (value: T | undefined) => String(value);
|
||||
const deserializer = (value: string) => value as unknown as T;
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const initializer = useRef((key: string) => {
|
||||
try {
|
||||
const localStorageValue = window.localStorage.getItem(key);
|
||||
// deserialize and return existing value if it's already been set
|
||||
const storedValue = window.localStorage.getItem(key);
|
||||
if (storedValue !== null) {
|
||||
return deserializer(storedValue);
|
||||
}
|
||||
|
||||
if (localStorageValue !== null) {
|
||||
return deserializer(localStorageValue);
|
||||
} else {
|
||||
initialValue && window.localStorage.setItem(key, serializer(initialValue));
|
||||
// item hasn't been set, but immediately set it to initialValue if provided
|
||||
if (initialValue) {
|
||||
window.localStorage.setItem(key, serializer(initialValue));
|
||||
return initialValue;
|
||||
}
|
||||
} catch (error) {
|
||||
@ -37,26 +42,20 @@ const useLocalStorage = <T>(
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const [state, setState] = useState<T | undefined>(() => initializer.current(key));
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
useLayoutEffect(() => setState(initializer.current(key)), [key]);
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
const set: Dispatch<SetStateAction<T | undefined>> = useCallback(
|
||||
(valOrFunc) => {
|
||||
try {
|
||||
// we need to support both T and (prevState: T) => T
|
||||
const newState = valOrFunc instanceof Function ? valOrFunc(state) : valOrFunc;
|
||||
|
||||
if (typeof newState === "undefined") {
|
||||
return;
|
||||
}
|
||||
|
||||
const value = typeof newState === "string" ? newState : JSON.stringify(newState);
|
||||
|
||||
window.localStorage.setItem(key, value);
|
||||
setState(deserializer(value));
|
||||
} catch (error) {} // eslint-disable-line no-empty
|
||||
window.localStorage.setItem(key, serializer(newState));
|
||||
setState(newState);
|
||||
} catch (error) {
|
||||
console.error(`failed to set localStorage item '${key}':`, error);
|
||||
}
|
||||
},
|
||||
[key, setState] // eslint-disable-line react-hooks/exhaustive-deps
|
||||
[key, state] // eslint-disable-line react-hooks/exhaustive-deps
|
||||
);
|
||||
|
||||
// eslint-disable-next-line react-hooks/rules-of-hooks
|
||||
@ -65,7 +64,7 @@ const useLocalStorage = <T>(
|
||||
window.localStorage.removeItem(key);
|
||||
setState(undefined);
|
||||
} catch (error) {} // eslint-disable-line no-empty
|
||||
}, [key, setState]);
|
||||
}, [key]);
|
||||
|
||||
return [state, set, remove];
|
||||
};
|
||||
|
Reference in New Issue
Block a user