Skip to content

Commit 0a2a228

Browse files
committed
Refactor useLocalStorage
1 parent 1dbaa2e commit 0a2a228

File tree

1 file changed

+52
-41
lines changed

1 file changed

+52
-41
lines changed

index.js

Lines changed: 52 additions & 41 deletions
Original file line numberDiff line numberDiff line change
@@ -791,59 +791,70 @@ export function useList(defaultList = []) {
791791
return [list, { set, push, removeAt, insertAt, updateAt, clear }];
792792
}
793793

794+
const dispatchStorageEvent = (key, newValue) => {
795+
window.dispatchEvent(new StorageEvent("storage", { key, newValue }));
796+
};
797+
798+
const setLocalStorageItem = (key, value) => {
799+
const stringifiedValue = JSON.stringify(value);
800+
window.localStorage.setItem(key, stringifiedValue);
801+
dispatchStorageEvent(key, stringifiedValue);
802+
};
803+
804+
const removeLocalStorageItem = (key) => {
805+
window.localStorage.removeItem(key);
806+
dispatchStorageEvent(key, null);
807+
};
808+
809+
const getLocalStorageItem = (key) => {
810+
return window.localStorage.getItem(key);
811+
};
812+
813+
const useLocalStorageSubscribe = (callback) => {
814+
window.addEventListener("storage", callback);
815+
return () => window.removeEventListener("storage", callback);
816+
};
817+
818+
const getLocalStorageServerSnapshot = () => {
819+
throw Error("useLocalStorage is a client-only hook");
820+
};
821+
794822
export function useLocalStorage(key, initialValue) {
795-
if (getEnvironment() === "server") {
796-
throw Error("useLocalStorage is a client-side only hook.");
797-
}
823+
const getSnapshot = () => getLocalStorageItem(key);
798824

799-
const readValue = React.useCallback(() => {
800-
try {
801-
const item = window.localStorage.getItem(key);
802-
return item ? JSON.parse(item) : initialValue;
803-
} catch (error) {
804-
console.warn(error);
805-
return initialValue;
806-
}
807-
}, [key, initialValue]);
825+
const store = React.useSyncExternalStore(
826+
useLocalStorageSubscribe,
827+
getSnapshot,
828+
getLocalStorageServerSnapshot
829+
);
808830

809-
const [localState, setLocalState] = React.useState(readValue);
810-
const handleSetState = React.useCallback(
811-
(value) => {
831+
const setState = React.useCallback(
832+
(v) => {
812833
try {
813-
const nextState =
814-
typeof value === "function" ? value(localState) : value;
815-
window.localStorage.setItem(key, JSON.stringify(nextState));
816-
setLocalState(nextState);
834+
const nextState = typeof v === "function" ? v(JSON.parse(store)) : v;
835+
836+
if (nextState === undefined || nextState === null) {
837+
removeLocalStorageItem(key);
838+
} else {
839+
setLocalStorageItem(key, nextState);
840+
}
817841
} catch (e) {
818842
console.warn(e);
819843
}
820844
},
821-
[key, localState]
845+
[key, store]
822846
);
823847

824-
const onStorageChange = React.useEffectEvent(() => {
825-
setLocalState(readValue());
826-
});
827-
828848
React.useEffect(() => {
829-
window.addEventListener("storage", onStorageChange);
830-
831-
return () => {
832-
window.removeEventListener("storage", onStorageChange);
833-
};
834-
}, []);
835-
836-
return [localState, handleSetState];
837-
}
849+
if (
850+
getLocalStorageItem(key) === null &&
851+
typeof initialValue !== "undefined"
852+
) {
853+
setLocalStorageItem(key, initialValue);
854+
}
855+
}, [key, initialValue]);
838856

839-
export function useLockBodyScroll() {
840-
React.useEffect(() => {
841-
const originalStyle = window.getComputedStyle(document.body).overflow;
842-
document.body.style.overflow = "hidden";
843-
return () => {
844-
document.body.style.overflow = originalStyle;
845-
};
846-
}, []);
857+
return [store ? JSON.parse(store) : initialValue, setState];
847858
}
848859

849860
export function useLogger(name, ...rest) {

0 commit comments

Comments
 (0)