React Hooks: useRef, useMemo, useCallback, and useContext
Beyond useState and useEffect, these hooks solve specific problems. Here's what interviews actually test.
useRef
useRef has two uses: accessing DOM elements, and storing mutable values that don't trigger re-renders.
// Use 1: DOM access
function FocusInput() {
const inputRef = useRef(null);
const focusIt = () => inputRef.current.focus();
return (
<>
<input ref={inputRef} type="text" />
<button onClick={focusIt}>Focus</button>
</>
);
}
// Use 2: mutable value without re-render
function Timer() {
const intervalId = useRef(null);
const start = () => {
intervalId.current = setInterval(() => {
console.log('tick');
}, 1000);
};
const stop = () => clearInterval(intervalId.current);
}useMemo — memoising expensive computations
useMemo caches the result of an expensive calculation and only recomputes when dependencies change.
function ProductList({ products, filterText }) {
// Without useMemo: re-computed on EVERY render
const filtered = products.filter(p =>
p.name.toLowerCase().includes(filterText.toLowerCase())
);
// With useMemo: only re-computed when products or filterText changes
const filtered = useMemo(() => {
return products.filter(p =>
p.name.toLowerCase().includes(filterText.toLowerCase())
);
}, [products, filterText]);
return <ul>{filtered.map(p => <li key={p.id}>{p.name}</li>)}</ul>;
}useCallback — stable function references
useCallback returns a memoised function. Useful when passing callbacks to optimised child components.
function Parent() {
const [count, setCount] = useState(0);
// Without useCallback: new function on every render
const handleClick = () => setCount(c => c + 1);
// With useCallback: same function reference until deps change
const handleClick = useCallback(() => {
setCount(c => c + 1);
}, []); // no deps: function never changes
return <ExpensiveChild onClick={handleClick} />;
}
// Only useful if ExpensiveChild is wrapped in React.memo:
const ExpensiveChild = React.memo(({ onClick }) => {
console.log('renders');
return <button onClick={onClick}>Click</button>;
});useContext — sharing state without prop drilling
useContext provides a way to pass data through the component tree without passing props at every level.
// 1. Create context
const ThemeContext = createContext('light');
// 2. Provide it high in the tree
function App() {
const [theme, setTheme] = useState('light');
return (
<ThemeContext.Provider value={{ theme, setTheme }}>
<Page />
</ThemeContext.Provider>
);
}
// 3. Consume it anywhere in the tree
function Button() {
const { theme, setTheme } = useContext(ThemeContext);
return (
<button
style={{ background: theme === 'dark' ? '#333' : '#fff' }}
onClick={() => setTheme(t => t === 'dark' ? 'light' : 'dark')}
>
Toggle theme
</button>
);
}Exam tip
The most common hooks interview question: "When do you use useMemo vs useCallback?" — useMemo caches a computed value; useCallback caches a function. Both only help when combined with React.memo on child components, otherwise they add overhead without benefit.
Think you're ready? Prove it.
Take the free React readiness test. Get a score, topic breakdown, and your exact weak areas.
Take the free React test →Free · No sign-up · Instant results