Extract reusable stateful logic without sharing state accidentally.
Read the explanation, run the worked example, then complete the matching Examples and Assignments tabs. Your original materials remain available alongside these notes.
Understand the idea
A custom Hook starts with use and may call other Hooks at its top level. Each caller gets its own state. Extract logic when it has a useful contract: useCounter returns a count and operations, while useTodolist returns tasks and add/remove/toggle operations. A custom Hook shares behavior, not one global store.
Your storage exercises introduce an external browser API. localStorage stores strings, so serialize and parse JSON. Read safely and handle malformed JSON or blocked storage. This example is for the browser-only Vite practice app. For server-rendered code, access storage after mounting and avoid hydration mismatches. Keep the storage key stable for a mounted Hook instance.
Run the example
Use a separate React practice project with a Node.js version supported by Vite. Create it once with these commands, then use its development URL. Do not paste this into the Edupoly website’s own source files.
npm create vite@latest react-practice -- --template react
cd react-practice
npm install
npm run dev
Replace src/App.jsx with the following complete example. Keep Vite’s generated src/main.jsx entry point.
src/App.jsx
import { useEffect, useState } from 'react';
function useStorageState(key, initialValue) {
const [value, setValue] = useState(() => {
try {
const saved = localStorage.getItem(key);
return saved === null ? initialValue : JSON.parse(saved);
} catch { return initialValue; }
});
useEffect(() => {
try { localStorage.setItem(key, JSON.stringify(value)); } catch { /* Storage may be unavailable. */ }
}, [key, value]);
return [value, setValue];
}
function useCounter(key) {
const [count, setCount] = useStorageState(key, 0);
return {count, increment: () => setCount(n => n + 1), reset: () => setCount(0)};
}
export default function App() {
const {count, increment, reset} = useCounter('practice-count-v1');
return <main><h1>{count}</h1><button onClick={increment}>Increment</button><button onClick={reset}>Reset</button></main>;
}
Expected result
Increment persists across page reloads in the same browser; Reset saves zero.
Apply this to your assignments
Build your useCounter, useTodolist and useStorageState exercises. Use different storage keys for independent lists; validate loaded data types in a production version. Test reload, invalid saved JSON, and storage being unavailable.
Your existing examples
- Custom-hooks
- Custom counter hook ‘useCounter'
- Custom Todolist hook ‘useTodolist’
- Custom localstoragestate hook ‘useStorageState’
- Counter with localstoragestate hook
- Todolist with localstoragestate hook
Open the Examples tab to run or inspect these supplied projects. Their original links are preserved.
Open Assignments for your complete original worksheet. Use the assignment workspace link to submit completed work.
Common mistakes
Do not call a Hook inside a click handler. Two callers using the same storage key do not automatically subscribe to one another’s updates.
Check your understanding
- Predict the output before running the example, then compare it with the result panel.
- Explain which value each expression reads and which action changes it.
- Try the boundary cases listed in the practice task and record what you observe.
- Rebuild one matching assignment without copying, then compare behavior with the original example.
Reference
Official documentation for this topic · Vite setup requirements
