React
Beginner
1 min read
Advanced Custom Hooks: usePrevious and useOnClickOutside
Example
import { useEffect, useRef } from 'react';
// ── usePrevious ────────────────────────────────────────────────────────────
function usePrevious(value) {
const ref = useRef(undefined);
// Runs AFTER the render, so ref still holds previous value during render
useEffect(() => {
ref.current = value;
});
return ref.current;
}
// ── useOnClickOutside ──────────────────────────────────────────────────────
function useOnClickOutside(ref, handler) {
useEffect(() => {
function listener(event) {
if (!ref.current || ref.current.contains(event.target)) return;
handler(event);
}
document.addEventListener('mousedown', listener);
document.addEventListener('touchstart', listener);
return () => {
document.removeEventListener('mousedown', listener);
document.removeEventListener('touchstart', listener);
};
}, [ref, handler]);
}
// ── Usage ──────────────────────────────────────────────────────────────────
function Dropdown({ items }) {
const [open, setOpen] = React.useState(false);
const containerRef = useRef(null);
useOnClickOutside(containerRef, () => setOpen(false));
return (
<div ref={containerRef}>
<button onClick={() => setOpen(o => !o)}>Menu</button>
{open && (
<ul>
{items.map(item => <li key={item}>{item}</li>)}
</ul>
)}
</div>
);
}
function CounterWithPrev() {
const [count, setCount] = React.useState(0);
const prev = usePrevious(count);
return (
<p>
Now: {count}, Before: {prev ?? 'n/a'}
<button onClick={() => setCount(c => c + 1)}>+</button>
</p>
);
}