I'm creating a react app that has a few states that I want to read/save to localStorage.
I've created a useLocalStorage
hook, and it's working, but I'm concerned that I'm reading from local storage every time the App
component re-renders.
I'd appreciate if someone could let me know if this is the case. I'd also appreciate any hints on if/how I could use useMemo
or useCallback
to improve this code.
App.jsx
import React from "react";
import { useLocalStorage } from "../useLocalStorage";
export default function App() {
const [saved1, setSaved1] = useLocalStorage("saved1", "The first test");
const [saved2, setSaved2] = useLocalStorage("saved2", "The second test");
return (
<div>
<p>{saved1}</p>
<input type="text" value={saved1} onChange={(e) => setSaved1(e.target.value)} />
<p>{saved2}</p>
<input type="text" value={saved2} onChange={(e) => setSaved2(e.target.value)} />
</div>
);
}
useLocalStorage.js
import { useState, useEffect } from "react";
export const useLocalStorage = (key, defaultValue) => {
console.log("Called useLocalStorage");
const [value, setValue] = useState(() => {
try {
const item = window.localStorage.getItem(key);
return item ? parseJSON(item) : defaultValue;
} catch (error) {
console.warn(`Error reading localStorage key “${key}”:`, error);
return defaultValue;
}
});
useEffect(() => {
console.log("Called useEffect");
try {
const newValue = value instanceof Function ? value(value) : value;
window.localStorage.setItem(key, JSON.stringify(newValue));
} catch (error) {
console.log(error);
}
}, [key, value]);
return [value, setValue];
};
// Wrapper to help handle undefined with JSON.parse
function parseJSON(value) {
try {
return value === "undefined" ? undefined : JSON.parse(value ?? "");
} catch {
console.log("parsing error on", { value });
return undefined;
}
}
There are other states in App
that I've left out for clarity. At the moment I'm getting Called useLocalStorage
console logged twice every time any of these states changes, e.g. toggling dark theme gives me the two console logs.