The same logic, everywhere
Saving a value to localStorage, tracking window size, toggling something open — you'll write the same useState + useEffect combo in many components. A custom hook packages it into one reusable function.
Write your own hook
import { useEffect, useState } from "react";
function useLocalStorage(key, initial) {
const [value, setValue] = useState(() => {
const saved = localStorage.getItem(key);
return saved !== null ? JSON.parse(saved) : initial;
});
useEffect(() => {
localStorage.setItem(key, JSON.stringify(value));
}, [key, value]);
return [value, setValue];
}
export default function Settings() {
const [name, setName] = useLocalStorage("name", "");
const [dark, setDark] = useLocalStorage("dark", false);
return (
<div>
<input value={name} onChange={(e) => setName(e.target.value)} placeholder="Name" />
<label>
<input type="checkbox" checked={dark} onChange={(e) => setDark(e.target.checked)} /> Dark mode
</label>
</div>
);
}- Hooks share *logic*, not state: two components calling useToggle() each get their own value.
- The same rules apply: call hooks at the top level, only from components or other hooks.
- Good candidates: useToggle, useDebounce, useFetch, useMediaQuery, useOnline.