In the modern web, Focus Management is the invisible backbone of User Experience (UX) and Accessibility (a11y). Whether it’s auto-focusing a search bar when a modal opens, or highlighting a field during a validation error, controlling focus is a requirement for any professional React application.
This guide will deconstruct the useFocus hook, explain its internal mechanics, and provide real-world production scenarios that will help you ace your next Senior React Developer interview.
1. The "Why": Beyond Native Focus
You might wonder: “Why do I need a custom hook? Can’t I just use the :focus pseudo-class in CSS?”
While CSS handles styling well, JavaScript logic often needs to "know" the focus state to:
Trigger Side Effects: Like fetching data when a user enters a field.
Coordinate UI: For example, showing a complex dropdown menu only when an input is active.
Programmatic Control: Moving the user's cursor automatically after an action (like clicking "Edit").
2. The Implementation: A 5/5 Rubric Approach
To score a 5/5 in an interview, your code must be performant, leak-proof, and ergonomic.
The Complete Code
JavaScript
import { useState, useRef, useEffect, useCallback } from 'react';
/**
* useFocus Hook
* @returns [ref, isFocused, setFocus]
*/
export function useFocus() {
const [isFocused, setIsFocused] = useState(false);
const elementRef = useRef(null);
// 1. Stable Function Identity
// We use useCallback so that if this function is passed to a memoized child,
// it doesn't cause unnecessary re-renders.
const setFocus = useCallback(() => {
if (elementRef.current) {
elementRef.current.focus();
}
}, []);
useEffect(() => {
// 2. Variable Capturing
// We copy the current ref value to a local variable to ensure
// the cleanup function refers to the correct node during unmount.
const node = elementRef.current;
if (!node) return;
const onFocus = () => setIsFocused(true);
const onBlur = () => setIsFocused(false);
// 3. Event Binding
node.addEventListener('focus', onFocus);
node.addEventListener('blur', onBlur);
// 4. Robust Cleanup
// This prevents memory leaks and "setting state on unmounted component" errors.
return () => {
node.removeEventListener('focus', onFocus);
node.removeEventListener('blur', onBlur);
};
}, []); // Empty deps ensure this only runs once on mount
return [elementRef, isFocused, setFocus];
}3. Deep Dive: Interview "Must-Knows"
A. The Reference Stability Trap
In the code above, setFocus is wrapped in useCallback. In an interview, explain that if setFocus were a plain function, it would be "re-created" on every render. If you passed that function as a prop to a component wrapped in React.memo, that component would re-render needlessly.
B. The Cleanup Closure
Why do we do const node = elementRef.current inside the useEffect? React refs are mutable. By the time the cleanup function (the return block) runs, elementRef.current might have already been set to null. By capturing it in a local variable node when the effect starts, we ensure we always have the correct reference to remove the listener from.
C. Performance vs. State
Every time setIsFocused is called, the component using the hook re-renders. In high-performance applications (like a spreadsheet with 1,000 inputs), you might discuss using ref only for focus state to avoid re-renders, though useState is the standard for general UI reactivity.
4. Real-World Production Scenarios
To impress an interviewer, you must connect the code to business value. Here is how companies like Airbnb, Netflix, and Stripe use focus management.
Scenario A: The "Command+K" Global Search (SaaS Strategy)
In platforms like Slack or Discord, users navigate via keyboard.
The Problem: The user presses a shortcut, but the search input is in a different part of the DOM tree.
The Solution: The
useFocushook provides thesetFocusmethod. When the global keydown event for "Cmd+K" is detected, the app callssetFocus().Production Detail: Use the
isFocusedboolean to trigger an "active" class on the parent container, creating a "Glow" effect that CSS:focuscan't easily reach if the input is nested deeply.
Scenario B: Multi-Factor Authentication (MFA) Inputs
When you receive a 6-digit code on your phone, banking apps like Revolut provide 6 individual boxes.
The Logic: As soon as the user types a digit in Box 1, the app validates the input and immediately calls
setFocus()on Box 2.The UI: The
isFocusedstate is used to change the border color of the current active box, providing clear visual feedback.
Scenario C: Form Validation and Accessibility (WCAG)
Accessibility is a legal requirement for many companies.
The Use Case: If a user submits a long registration form and the "Email" field is invalid, the page shouldn't just show a red label. It should move the user's focus back to that field.
The Implementation: Using
useFocus, you can programmatically snap the user back to the error, allowing screen readers to immediately announce the problematic field.
5. Advanced Interview Question: "What about Ref Forwarding?"
A common follow-up question is: "How do you use this hook if the input is a custom component, not a plain <input />?"
You must mention React.forwardRef. This allows your custom component to receive the ref from the hook and "forward" it down to the underlying HTML element.
JavaScript
const MyInput = React.forwardRef((props, ref) => (
<input ref={ref} {...props} className="custom-input" />
));
function Parent() {
const [inputRef, isFocused] = useFocus();
return <MyInput ref={inputRef} />;
}6. Comparison: useFocus vs. Native autoFocus
Interviewers love testing your knowledge of "Native vs. Synthetic."
Native
autoFocus: Runs only on the initial page load. It is often unreliable in Single Page Applications (SPAs) where components mount/unmount without a page refresh.useFocus(Our Hook): Gives you full control. You can trigger focus based on logic (e.g., "Focus only if the user is a first-time visitor") and handle cleanups properly.
7. The "Lead Developer" Perspective: Edge Cases
To truly score 100%, mention these two edge cases:
Server-Side Rendering (SSR): Remind the interviewer that
useEffectonly runs on the client. Therefore,elementRef.currentwill benullon the server, and the hook safely handles this without crashing.Focus Trapping: In production modals, you don't just need to focus an element; you need to keep focus inside the modal. Explain how
useFocuscould be a building block for auseFocusTraphook that prevents the user from "Tabbing" out of a popup.
Summary Checklist for the Interview
Rubric Category | Requirement | Why it matters |
Correctness | Returns | Standard React hook pattern (similar to |
Cleanup |
| Prevents memory leaks in long-running SPAs. |
Performance |
| Prevents unnecessary re-renders of child components. |
Robustness | Check | Prevents "Cannot read property of null" errors. |
UX/A11y | Mentioning WCAG and focus shifting | Shows you care about the user, not just the code. |
Final Thought
When implementing useFocus, you aren't just writing a utility; you are building a bridge between the user's intent and the browser's behavior. By focusing on stability and accessibility, you demonstrate the maturity required for high-level engineering roles.