Discover how custom hooks can save you hours of debugging and rewriting code!
Why Custom hook best practices in React? - Purpose & Use Cases
Imagine copying and pasting the same state logic across multiple React components to handle user input or data fetching.
This manual repetition leads to bugs, inconsistent behavior, and makes your code hard to maintain or update.
Custom hooks let you extract and share reusable logic cleanly, so your components stay simple and consistent.
function ComponentA() { const [count, setCount] = React.useState(0); React.useEffect(() => { console.log(count); }, [count]); return <button onClick={() => setCount(count + 1)}>Click</button>; } function ComponentB() { const [count, setCount] = React.useState(0); React.useEffect(() => { console.log(count); }, [count]); return <button onClick={() => setCount(count + 1)}>Click</button>; }function useCounter() { const [count, setCount] = React.useState(0); React.useEffect(() => { console.log(count); }, [count]); return [count, setCount]; } function ComponentA() { const [count, setCount] = useCounter(); return <button onClick={() => setCount(count + 1)}>Click</button>; } function ComponentB() { const [count, setCount] = useCounter(); return <button onClick={() => setCount(count + 1)}>Click</button>; }It enables clean, reusable, and maintainable code by sharing logic across components effortlessly.
Sharing form validation logic between multiple input fields without repeating code in each component.
Custom hooks help avoid code duplication.
They keep components focused on UI, not logic.
Following best practices makes your app easier to maintain.
Practice
Solution
Step 1: Understand React hook naming conventions
React requires custom hooks to start withuseso React can identify them as hooks.Step 2: Evaluate the options
Only Start the hook name withuseto follow React conventions correctly follows this convention by starting the name withuse.Final Answer:
Start the hook name withuseto follow React conventions -> Option AQuick Check:
Hook names start with 'use' = B [OK]
- Naming hooks without 'use' prefix
- Using 'hook' prefix instead of 'use'
- Starting hook names with lowercase letters without 'use'
Solution
Step 1: Check the hook naming and return type
The hook must start withuseand return state and updater properly. function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } usesuseCountand returns an array with count and increment function.Step 2: Verify state management correctness
function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } usesuseStatecorrectly and returns a function that updates state withsetCount(count + 1). This is the correct pattern.Final Answer:
function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } -> Option CQuick Check:
Custom hook syntax with useState and return array = C [OK]
- Not starting function name with 'use'
- Returning object instead of array without reason
- Not using useState for state management
- Mutating state directly instead of using setter
function useToggle(initial) {
const [on, setOn] = useState(initial);
const toggle = () => setOn(prev => !prev);
return [on, toggle];
}
function Component() {
const [isOn, toggle] = useToggle(false);
toggle();
return {isOn ? 'ON' : 'OFF'};
}What will the component render after calling
toggle() once?Solution
Step 1: Analyze where toggle is called
toggle()is called directly in the component body, which executes during every render phase.Step 2: Understand React render rules
State updates during render trigger re-renders. Sincetoggle()is unconditional, it flips state every render, causing an infinite loop. React prohibits this and produces errors/warnings (e.g., "Cannot update a component while rendering a different component" or too many re-renders).Final Answer:
Error because toggle is called outside an event -> Option DQuick Check:
State updaters only in events/effects, not render body [OK]
- Assuming async update shows initial state without considering re-render loop
- Expecting 'ON' due to toggle flip
- Confusing boolean with string output
- Thinking render body calls are safe without infinite loop
function useCounter() {
let count = 0;
const increment = () => { count += 1; };
return { count, increment };
}Solution
Step 1: Check state management in the hook
The hook uses a local variablecountthat resets to 0 on every call. It does not useuseState, so React won't track changes or re-render.Step 2: Understand React state importance
WithoutuseState, updatingcountdoes not cause component updates, making the hook ineffective.Final Answer:
It does not use React state, so count won't trigger re-renders -> Option AQuick Check:
Local variable won't update UI without useState [OK]
- Using local variables instead of useState
- Expecting UI to update without state
- Returning object instead of array (not always wrong)
- Misnaming hook without 'use' prefix
useFetch that fetches data from a URL and returns loading state, data, and error. Which approach follows best practices?Solution
Step 1: Understand data fetching in hooks
Fetching data should happen insideuseEffectto avoid running on every render. State variables track loading, data, and error.Step 2: Evaluate options for best practice
UseuseEffectinside the hook to fetch data on mount, useuseStatefor loading, data, and error, and return them as an object correctly usesuseEffectanduseStateto manage asynchronous fetch and returns all relevant states as an object for clarity.Final Answer:
UseuseEffectinside the hook to fetch data on mount, useuseStatefor loading, data, and error, and return them as an object -> Option BQuick Check:
Fetch in useEffect with state management = A [OK]
- Fetching data directly in hook body causing infinite loops
- Not managing loading or error states
- Using useMemo for side effects
- Passing fetched data as parameter instead of fetching inside hook
Ready to prove it?
Pass the test and this pattern moves unknown
