Frontend Engineering
React Hooks Deep Dive
Quiz: Hooks Dependency Trap
intermediate12 min read
Test Your Dependency Array Intuition
Dependency arrays look simple. They are not. Each question below involves a useEffect dependency array. Don't guess — trace the closure behavior, Object.is comparisons, and effect timing.
If you score below 6 out of 8, go back to the useEffect, stale closures, and useMemo lessons. These are the bugs that will eat your afternoon.
Question 1: The Missing Dependency
function Timer({ delay }) {
const [count, setCount] = useState(0);
useEffect(() => {
const id = setInterval(() => {
setCount(count + 1);
}, delay);
return () => clearInterval(id);
}, [delay]);
return <p>{count}</p>;
}
Quiz
Question 2: Object in Dependencies
function UserList({ filters }) {
const [users, setUsers] = useState([]);
useEffect(() => {
fetchUsers(filters).then(setUsers);
}, [filters]);
return <ul>{users.map(u => <li key={u.id}>{u.name}</li>)}</ul>;
}
// Parent:
function App() {
return <UserList filters={{ role: 'admin', active: true }} />;
}
Quiz
Question 3: Function in Dependencies
function SearchResults({ query }) {
const [results, setResults] = useState([]);
function fetchResults() {
return fetch(`/api/search?q=${query}`).then(r => r.json());
}
useEffect(() => {
fetchResults().then(setResults);
}, [fetchResults]);
return <div>{results.length} results</div>;
}
Quiz
Question 4: Ref in Dependencies
function Component() {
const inputRef = useRef(null);
useEffect(() => {
inputRef.current?.focus();
}, [inputRef]);
return <input ref={inputRef} />;
}
Quiz
Question 5: Stable Dispatch
function TodoApp() {
const [todos, dispatch] = useReducer(todosReducer, []);
useEffect(() => {
const saved = JSON.parse(localStorage.getItem('todos') || '[]');
if (saved.length) {
dispatch({ type: 'LOAD', todos: saved });
}
}, [dispatch]);
return <TodoList todos={todos} dispatch={dispatch} />;
}
Quiz
Question 6: Cleanup Timing
function Logger({ page }) {
useEffect(() => {
console.log(`Entered: ${page}`);
return () => {
console.log(`Left: ${page}`);
};
}, [page]);
return <div>Page: {page}</div>;
}
// page changes: 'home' → 'about' → 'contact'
Quiz
Question 7: Derived State Anti-pattern
function FilteredList({ items, query }) {
const [filtered, setFiltered] = useState([]);
useEffect(() => {
setFiltered(items.filter(i => i.name.includes(query)));
}, [items, query]);
return <ul>{filtered.map(i => <li key={i.id}>{i.name}</li>)}</ul>;
}
Quiz
Question 8: The Double Subscription
function Chat({ roomId }) {
const [messages, setMessages] = useState([]);
useEffect(() => {
function handleMessage(msg) {
setMessages(prev => [...prev, msg]);
}
const ws = new WebSocket(`wss://chat.example.com/${roomId}`);
ws.addEventListener('message', handleMessage);
return () => {
ws.removeEventListener('message', handleMessage);
ws.close();
};
}, [roomId]);
return <MessageList messages={messages} />;
}
Quiz
Scoring Guide
| Score | Assessment |
|---|---|
| 8/8 | You understand effect lifecycle at a deep level. Ready for production React. |
| 6-7 | Solid understanding with minor gaps. Review the scenarios you missed. |
| 4-5 | Common patterns are shaky. Revisit useEffect, closures, and referential equality. |
| 0-3 | Go back to the fundamentals. Focus on: closure capture, Object.is, and effect timing. |