Extract and reuse stateful logic across components with custom hooks.
A custom hook is a function whose name starts with use that calls other hooks. It lets you extract stateful logic from a component and reuse it anywhere.
// Before — logic stuck inside the component
function SearchPage() {
const [query, setQuery] = useState("");
const stored = localStorage.getItem("lastQuery");
// ...
// After — extracted into a reusable hook
function useLocalStorage<T>(key: string, initial: T) {
const [value, setValue] = useState<T>(() => {
try {
const item = localStorage.getItem(key);
return item ? JSON.parse(item) : initial;
} catch {
return initial;
}
});
function set(newValue: T) {
setValue(newValue);
localStorage.setItem(key, JSON.stringify(newValue));
}
return [value, set] as const;
}
Custom hooks follow the same rules:
function useCounter(initial = 0) {
const [count, setCount] = useState(initial);
const increment = () => setCount(c => c + 1);
const decrement = () => setCount(c => c - 1);
const reset = () => setCount(initial);
return { count, increment, decrement, reset };
}
// Using it — clean and readable:
function App() {
const { count, increment, reset } = useCounter(10);
return (
<>
<p>{count}</p>
<button onClick={increment}>+</button>
<button onClick={reset}>Reset</button>
</>
);
}
useCounter custom hook that accepts an initial number (default 0) and returns { count, increment, decrement, reset }useCounter inside App to render a counter with Increment, Decrement, and Reset buttons<p> must display the current countimport { useState } from "react"; // Create useCounter hook here function useCounter(initial = 0) { // implement: return { count, increment, decrement, reset } } export default function App() { const { count, increment, decrement, reset } = useCounter(0); return ( <div> <p>{count}</p> <button onClick={decrement}>Decrement</button> <button onClick={reset}>Reset</button> <button onClick={increment}>Increment</button> </div> ); }