Q160: Custom Hooks and Reusable Stateful Logic
What Interviewers Want To Evaluate
This question checks whether you can extract reusable behavior without hiding ownership. Interviewers want hook rules, naming, stateful logic extraction, effects inside hooks, return shape, dependency design, testing, and avoiding over-abstraction.
Senior answers should explain custom hooks as behavior composition, not component sharing.
Short Interview Answer
A custom hook is a function whose name starts with use and can call other hooks. It extracts reusable stateful logic from components. Custom hooks do not share state automatically between callers; each call gets its own hook state. They are useful for subscriptions, forms, async state, media queries, local storage, and reusable event behavior. Good hooks have clear inputs, clear return values, cleanup, and minimal hidden side effects.
Detailed Interview Answer
Custom hooks let you package hook logic.
function useWindowWidth() {
const [width, setWidth] = useState(() => window.innerWidth);
useEffect(() => {
function onResize() {
setWidth(window.innerWidth);
}
window.addEventListener("resize", onResize);
return () => window.removeEventListener("resize", onResize);
}, []);
return width;
}
The component uses behavior, not implementation details.
Hook Rules
Custom hooks follow the same rules:
call hooks only at top level
call hooks only from React components or other hooks
do not call hooks conditionally
use the use prefix
The prefix lets tooling enforce rules.
State Is Per Call
Custom hooks do not automatically create shared global state.
const a = useCounter();
const b = useCounter();
Each call has separate state unless the hook uses an external shared store.
Good Extraction Signals
Extract a custom hook when:
multiple components repeat stateful logic
an effect has clear reusable ownership
browser API integration is repeated
async status handling repeats
form field behavior repeats
component is hard to scan
testing behavior separately helps
Do not extract only to make a file look shorter if the abstraction has no name.
Return Shape
Return shape should match usage.
Tuple style is good for small familiar APIs:
const [value, setValue] = useLocalStorageState("theme", "dark");
Object style is better when there are several named values:
const { data, error, status, reload } = useUser(userId);
Effects Inside Hooks
Hooks can own effects and cleanup.
function useEventListener(target, eventName, listener) {
useEffect(() => {
target.addEventListener(eventName, listener);
return () => target.removeEventListener(eventName, listener);
}, [target, eventName, listener]);
}
The hook should make dependencies and cleanup behavior clear.
Hidden Side Effects
A custom hook can become dangerous if it hides surprising behavior.
useUser(userId);
Does it fetch? Subscribe? Write cache? Redirect? Track analytics?
Names and docs should make behavior obvious.
Dependency Design
Hooks should avoid forcing callers into unstable dependencies.
If a hook accepts callbacks, consider whether callers need to memoize them or whether the hook should support latest-callback patterns.
The contract should be explicit.
Common Mistakes
A common mistake is thinking custom hooks share state like services.
Another mistake is hiding too many responsibilities in one hook.
Another mistake is returning unstable objects that trigger downstream effects every render.
Senior Trade-Offs
Custom hooks are composition tools. They can make components clean, but too many vague hooks create an invisible framework inside the app.
Prefer hooks with clear names, single responsibility, explicit inputs, and predictable cleanup.
Code or Design Example
function useToggle(initialValue = false) {
const [value, setValue] = useState(initialValue);
const toggle = useCallback(() => {
setValue((value) => !value);
}, []);
return { value, setValue, toggle };
}
This hook packages a small reusable state transition.
Debugging Workflow
When a custom hook causes bugs:
check hook rules
check effect dependencies
check cleanup
check whether state is shared or per call
check hidden side effects
check return identity
check whether abstraction is doing too much
test hook behavior independently
Interview Framing
Define a custom hook as reusable stateful logic. Then discuss hook rules, per-call state, effects, return shape, and abstraction trade-offs.
Revision Notes
Custom hooks compose behavior. They are not magic shared state and not a dumping ground for complexity.
Key Takeaways
The best custom hooks make ownership clearer. The worst ones hide control flow.
Follow-Up Questions
- What makes a function a custom hook?
- Can custom hooks call other hooks?
- Do custom hooks share state automatically?
- Why must hooks use the use prefix?
- When should logic be extracted into a hook?
- Tuple return or object return?
- Can effects live inside custom hooks?
- How do hooks handle cleanup?
- What makes a hook over-abstracted?
- How do you test custom hooks?
How I Would Answer This In A Real Interview
I would say custom hooks extract reusable stateful logic while obeying hook rules. Each call has its own state, effects inside hooks need clear cleanup and dependencies, and the abstraction should make ownership easier to understand.