React  

Deep Dive into React Hooks: useEffect, useReducer, and useMemo

React introduced hooks to bring state management, side effects, and performance optimizations to functional components. Among the most important ones are useEffect, useReducer, and useMemo.

Let’s explore them in detail with icons, syntax, examples, and best practices.

⚡ useEffect – Side Effects

🛠 What does it do?

  • Handles side effects (things outside the component’s render).
  • Examples include data fetching, subscriptions, timers, and manual DOM updates.

📑 Syntax

useEffect(() => {
  // effect logic here

  return () => {
    // cleanup logic here (optional)
  };
}, [dependencies]);

📌 Explanation of Dependencies

  • [] empty array: Runs only once (like componentDidMount).
  • [state, props]: Runs when listed values change.
  • No array: Runs after every render.

🖼 Example

import { useState, useEffect } from "react";

function Timer() {
  const [count, setCount] = useState(0);

  useEffect(() => {
    const timer = setInterval(() => setCount(c => c + 1), 1000);

    return () => clearInterval(timer); // cleanup
  }, []);

  return <h1>Time: {count}s</h1>;
}

✅ Best Use Cases

  • Fetching API data.
  • Setting up subscriptions (WebSocket, Firebase, etc.).
  • Handling intervals/timers.
  • Cleaning up on unmount.

🎛 useReducer – Complex State Management

🛠 What does it do?

  • Manages complex or structured state in components.
  • Works with a reducer function that defines how state updates happen.
  • Inspired by Redux but scoped to the component.

📑 Syntax

const [state, dispatch] = useReducer(reducer, initialState);

📌 Reducer Function

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

🖼 Example

import { useReducer } from "react";

const initialState = { count: 0 };

function reducer(state, action) {
  switch (action.type) {
    case "increment":
      return { count: state.count + 1 };
    case "decrement":
      return { count: state.count - 1 };
    default:
      return state;
  }
}

function Counter() {
  const [state, dispatch] = useReducer(reducer, initialState);

  return (
    <>
      <h1>Count: {state.count}</h1>
      <button onClick={() => dispatch({ type: "increment" })}>+</button>
      <button onClick={() => dispatch({ type: "decrement" })}>-</button>
    </>
  );
}

✅ Best Use Cases

  • Forms with multiple inputs.
  • State objects with multiple properties.
  • Complex logic where multiple actions update the state differently.

🚀 useMemo – Performance Optimization

🛠 What does it do?

  • Memoizes (caches) results of expensive calculations.
  • Prevents recalculating unless dependencies change.
  • Improves performance by skipping unnecessary work.

📑 Syntax

const memoizedValue = useMemo(
  () => computeExpensiveValue(a, b),
  [a, b]
);

🖼 Example

import { useState, useMemo } from "react";

function ExpensiveCalculation(num) {
  console.log("Calculating...");
  let result = 0;
  for (let i = 0; i < 1000000000; i++) {
    result += num;
  }
  return result;
}

function App() {
  const [count, setCount] = useState(0);
  const [number, setNumber] = useState(5);

  const calculation = useMemo(() => ExpensiveCalculation(number), [number]);

  return (
    <>
      <h1>Calculation: {calculation}</h1>
      <button onClick={() => setCount(c => c + 1)}>Re-render {count}</button>
      <button onClick={() => setNumber(n => n + 1)}>Increase Number</button>
    </>
  );
}

✅ Best Use Cases

  • Expensive computations (large loops, heavy calculations).
  • Derived data that depends on stable inputs.
  • Avoid unnecessary recalculations on every render.
Hook 🔍 Purpose ⏳ When It Runs 🎯 Best Use Case ⚠️ Pitfalls
⚡ useEffect Handle side effects (API calls, timers, subscriptions, DOM updates) After render (depends on dependency array) Data fetching, event listeners, intervals, and cleanup Infinite loops if the dependency array is wrong
🎛 useReducer Manage complex state using reducer & actions When dispatch() is called Forms, counters, multi-step state logic Can be overkill for a simple state
🚀 useMemo Cache expensive calculations to improve performance When dependencies change Heavy calculations, derived values Don’t use for every small calculation (can add overhead)

🔑 Key Takeaways

  • useEffect: Think side effects (runs after render).
  • useReducer: Think complex state logic (like Redux inside a component).
  • useMemo: Think performance optimization (cache results).

Together, these hooks make React components.

  • 👉 Smarter (logic with useReducer)
  • 👉 Cleaner (side effects with useEffect)
  • 👉 Faster (performance boost with useMemo)