Javascript

JavaScript Learning Guide: 10 – React Hooks and Side Effects

Language

Master Table of Contents

Who this chapter is for

  • Learners who already know React basics and are ready for real app logic
  • Beginners who keep getting confused by useEffect behavior
  • Anyone who has seen duplicate requests, stale values, or weird rerender loops

What you’ll learn

  • Better useState patterns for object/array updates
  • Correct useEffect usage and dependency thinking
  • How and when to extract reusable logic into custom hooks

Why this topic matters

Most production React bugs come from side effects, not JSX. Data fetching, timers, subscriptions, and browser sync all live in this area.

If you understand hooks deeply, your app becomes predictable. If not, you get classic issues: repeated API calls, stale UI, and hard-to-debug state behavior.

Core concepts

useState patterns

  • Derived updates and object/array updates
  • Avoid stale closure mistakes

Use functional updates (setState(prev => ...)) when the next value depends on previous state.

useEffect

  • Runs after render
  • Dependency array controls when effect runs

Quick mental model:

  • No dependency array -> runs after every render
  • Empty array ([]) -> runs once on mount
  • With dependencies -> runs when those values change

Custom hooks

  • Extract repeated state/effect logic
  • Improve readability and reuse

A custom hook is not “advanced magic.” It is just a function that packages repeated hook logic cleanly.

Step-by-step walkthrough

Step 1 — Add effect-driven behavior

Start with one mount-time fetch and one explicit loading state.

Step 2 — Manage dependencies safely

Audit dependencies intentionally. Include what the effect really uses.

Step 3 — Extract into custom hook

Move repeated fetch + loading + error logic into a custom hook like useItems.

Target in this chapter: make side effects understandable and predictable.

Practical examples

Example 1 — Effect with fetch

useEffect(() => {
  let active = true;
  async function run() {
    const response = await fetch("/api/items");
    const data = await response.json();
    if (active) setItems(data);
  }
  run();
  return () => {
    active = false;
  };
}, []);

Why this matters:

  • The cleanup guard helps prevent state updates from stale async responses.

Example 2 — Custom hook skeleton

function useToggle(initial = false) {
  const [value, setValue] = useState(initial);
  const toggle = () => setValue((prev) => !prev);
  return { value, toggle };
}

Real case:

  • Hooks like this reduce repeated boilerplate in UI controls and settings panels.

Common mistakes and how to avoid them

  • Missing dependencies in useEffect -> stale data and inconsistent behavior
  • Putting too many concerns in one effect -> split effects by responsibility
  • Repeating fetch logic across components -> extract custom hooks
  • Triggering race conditions in search/fetch flows -> ignore stale responses or cancel old requests

Mini Project

  • Build a live search app with:
  • debounced input,
  • request-based loading state,
  • error fallback,
  • stale-response protection.

Bonus:

  • Move fetch logic into a useSearch custom hook.

Quick practice

  • Add loading and error state to effect-based fetch
  • Implement input debounce before request
  • Extract request logic into one custom hook
  • Explain in one sentence: when should one effect be split into two effects?

Key takeaways

  • Hooks are powerful when logic is explicit and scoped well
  • useEffect is about synchronization, not random side-effect dumping
  • Custom hooks improve reuse and keep components focused

Next step

Continue to [11. Forms and Validation in React].

No Comments

Leave a Reply

This site uses Akismet to reduce spam. Learn how your comment data is processed.