Challenge: Dropdown menu
- React course
- Beginner
- Lesson 84/84
- 6 min
Write a dropdown that closes on outside click using useRef and state.
Challenge: Dropdown menu
This challenge asks you to write a Dropdown component. A button and a list that shows when open. Closes on outside click.
Combine two hooks: useState for the open state, useRef to detect outside clicks. On button click, toggle with setOpen(o => !o).
For outside click, in useEffect add document.addEventListener("click", ...); if the clicked target is not inside the menu, call setOpen(false). Cleanup removes the listener.
Practice Write Dropdown
Complete the skeleton. Write a Dropdown component. Create open state and ref = useRef(null). On button click, setOpen(o => !o). In useEffect, add document.addEventListener("click", handler); the handler calls setOpen(false) on outside click. Remove the listener on cleanup. When open is true, show <ul><li>A</li><li>B</li></ul>. Export it.
Starter code
import { useState, useRef, useEffect } from "react";
// Write the Dropdown component hereSample solution
import { useState, useRef, useEffect } from "react";
function Dropdown() {
const [open, setOpen] = useState(false);
const ref = useRef(null);
useEffect(() => {
const handler = (e) => {
if (ref.current && !ref.current.contains(e.target)) setOpen(false);
};
document.addEventListener("click", handler);
return () => document.removeEventListener("click", handler);
}, []);
return (
<div ref={ref}>
<button onClick={() => setOpen((o) => !o)}>Menu</button>
{open && (
<ul>
<li>A</li>
<li>B</li>
</ul>
)}
</div>
);
}
export default Dropdown;Questions and answers
Review the lesson questions together with their answers. To test yourself, solve the lesson interactively.
1 How is outside click caught?
- document.addEventListener Correct answer
- window.alert
- console.log
- useState
Explanation: Outside click is caught with document.addEventListener.
2 Match the dropdown parts
| Term | Meaning |
|---|---|
useState |
open state |
useRef |
Outside click |
useEffect |
Listener |
setOpen(false) |
Closes |