Bird
Raised Fist0
Reactframework~10 mins

Custom hook best practices in React - Step-by-Step Execution

Choose your learning style10 modes available
Concept Flow - Custom hook best practices
Start: Component needs reusable logic
↓
Create custom hook function
↓
Use React hooks inside custom hook
↓
Return needed state and functions
↓
Use custom hook in component
↓
Component uses returned values
↓
Update state triggers re-render
↓
End
This flow shows how a component uses a custom hook to share logic, with the hook managing state and returning values for the component to use.
Execution Sample
React
import { useState, useEffect } from 'react';

function useWindowWidth() {
  const [width, setWidth] = useState(window.innerWidth);
  useEffect(() => {
    const handleResize = () => setWidth(window.innerWidth);
    window.addEventListener('resize', handleResize);
    return () => window.removeEventListener('resize', handleResize);
  }, []);
  return width;
}

function MyComponent() {
  const width = useWindowWidth();
  return <div>Width: {width}px</div>;
}
This code defines a custom hook to track window width and uses it in a component to display the current width.
Execution Table
StepActionState BeforeState AfterComponent Render Output
1MyComponent mounts, calls useWindowWidthwidth: undefinedwidth: window.innerWidth (e.g. 1024)Width: 1024px
2useEffect adds resize event listenerwidth: 1024width: 1024Width: 1024px
3Window resized to 800px, event triggers handleResizewidth: 1024width: 800Width: 800px
4Component re-renders with new widthwidth: 800width: 800Width: 800px
5Component unmounts, cleanup removes event listenerwidth: 800N/AComponent removed
6No further updatesN/AN/ANo output
💡 Component unmounts and event listener is cleaned up, stopping updates.
Variable Tracker
VariableStartAfter Step 1After Step 3Final
widthundefined1024800800
Key Moments - 3 Insights
Why do we add and remove the event listener inside useEffect?
Because useEffect runs after the component mounts, adding the listener once, and returns a cleanup function to remove it when the component unmounts, preventing memory leaks. See execution_table steps 2 and 5.
Why must the custom hook name start with 'use'?
React relies on the 'use' prefix to identify hooks and enforce rules. Without it, React won't apply hook behavior correctly, causing errors.
What happens if we don't return values from the custom hook?
The component won't get the needed data or functions to use, so it can't respond to state changes. Returning values connects the hook's logic to the component. See execution_table step 1.
Visual Quiz - 3 Questions
Test your understanding
Look at the execution table, what is the value of 'width' after step 3?
A800
B1024
Cundefined
D0
💡 Hint
Check the 'State After' column for step 3 in the execution_table.
At which step is the event listener removed?
AStep 4
BStep 5
CStep 2
DStep 6
💡 Hint
Look for cleanup actions in the execution_table rows.
If the custom hook did not return 'width', what would the component render?
AWidth: 0px
BWidth: 1024px
CWidth: undefinedpx
DAn error
💡 Hint
Refer to variable_tracker and how the component uses the returned value.
Concept Snapshot
Custom hooks start with 'use' and contain React hooks.
Use useEffect for side effects and cleanup.
Return state and functions to share logic.
Use them inside components like normal hooks.
They help reuse logic cleanly and keep components simple.
Full Transcript
Custom hooks in React are functions that start with 'use' and let you reuse stateful logic across components. They use React hooks like useState and useEffect inside. When a component calls a custom hook, it gets back state or functions to use. The hook can set up side effects like event listeners and clean them up when the component unmounts. This keeps code organized and avoids repeating logic. For example, a useWindowWidth hook tracks the window size and updates state on resize. The component uses this hook to display the current width. The hook adds the resize listener on mount and removes it on unmount to prevent memory leaks. Returning values from the hook connects its logic to the component's render. Always name custom hooks starting with 'use' so React treats them correctly.

Practice

(1/5)
1. Which of the following is a best practice when naming a custom React hook?
easy
A. Start the hook name with use to follow React conventions
B. Name the hook with a capital letter but no prefix
C. Use any name as long as it describes the functionality
D. Prefix the hook name with hook instead of use

Solution

  1. Step 1: Understand React hook naming conventions

    React requires custom hooks to start with use so React can identify them as hooks.
  2. Step 2: Evaluate the options

    Only Start the hook name with use to follow React conventions correctly follows this convention by starting the name with use.
  3. Final Answer:

    Start the hook name with use to follow React conventions -> Option A
  4. Quick Check:

    Hook names start with 'use' = B [OK]
Hint: Always start custom hook names with 'use' [OK]
Common Mistakes:
  • Naming hooks without 'use' prefix
  • Using 'hook' prefix instead of 'use'
  • Starting hook names with lowercase letters without 'use'
2. Which of the following is the correct syntax to define a custom hook that returns a count state and an increment function?
easy
A. function useCount() { const [count, setCount] = useState(0); return { count increment: () => setCount(count + 1) }; }
B. function countHook() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; }
C. function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; }
D. function useCount() { const count = 0; function increment() { count += 1; } return { count, increment }; }

Solution

  1. Step 1: Check the hook naming and return type

    The hook must start with use and return state and updater properly. function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } uses useCount and returns an array with count and increment function.
  2. Step 2: Verify state management correctness

    function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } uses useState correctly and returns a function that updates state with setCount(count + 1). This is the correct pattern.
  3. Final Answer:

    function useCount() { const [count, setCount] = useState(0); return [count, () => setCount(count + 1)]; } -> Option C
  4. Quick Check:

    Custom hook syntax with useState and return array = C [OK]
Hint: Custom hooks return state and updater, named starting with 'use' [OK]
Common Mistakes:
  • 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
3. Given this custom hook code:
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?
medium
A.
ON
B.
false
C.
OFF
D. Error because toggle is called outside an event

Solution

  1. Step 1: Analyze where toggle is called

    toggle() is called directly in the component body, which executes during every render phase.
  2. Step 2: Understand React render rules

    State updates during render trigger re-renders. Since toggle() 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).
  3. Final Answer:

    Error because toggle is called outside an event -> Option D
  4. Quick Check:

    State updaters only in events/effects, not render body [OK]
Hint: Never call state updaters during render; use events or useEffect [OK]
Common Mistakes:
  • 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
4. What is wrong with this custom hook code?
function useCounter() {
  let count = 0;
  const increment = () => { count += 1; };
  return { count, increment };
}
medium
A. It does not use React state, so count won't trigger re-renders
B. The hook name should not start with 'use'
C. The increment function should not modify count
D. It should return an array, not an object

Solution

  1. Step 1: Check state management in the hook

    The hook uses a local variable count that resets to 0 on every call. It does not use useState, so React won't track changes or re-render.
  2. Step 2: Understand React state importance

    Without useState, updating count does not cause component updates, making the hook ineffective.
  3. Final Answer:

    It does not use React state, so count won't trigger re-renders -> Option A
  4. Quick Check:

    Local variable won't update UI without useState [OK]
Hint: Always use useState for reactive state in hooks [OK]
Common Mistakes:
  • 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
5. You want to create a custom hook useFetch that fetches data from a URL and returns loading state, data, and error. Which approach follows best practices?
hard
A. Fetch data directly in the hook body without useEffect, return data and error only
B. Use useEffect inside the hook to fetch data on mount, use useState for loading, data, and error, and return them as an object
C. Use useMemo to fetch data and return loading and data states
D. Fetch data outside the hook and pass it as a parameter to the hook

Solution

  1. Step 1: Understand data fetching in hooks

    Fetching data should happen inside useEffect to avoid running on every render. State variables track loading, data, and error.
  2. Step 2: Evaluate options for best practice

    Use useEffect inside the hook to fetch data on mount, use useState for loading, data, and error, and return them as an object correctly uses useEffect and useState to manage asynchronous fetch and returns all relevant states as an object for clarity.
  3. Final Answer:

    Use useEffect inside the hook to fetch data on mount, use useState for loading, data, and error, and return them as an object -> Option B
  4. Quick Check:

    Fetch in useEffect with state management = A [OK]
Hint: Fetch inside useEffect, manage state with useState [OK]
Common Mistakes:
  • 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
Recommended

Ready to prove it?

Pass the test and this pattern moves unknown strong while earning +1 Grip toward your certificate.

10 questions~5 mineasy hard