Popular Searches
Popular Course Categories
Popular Courses

Common Hook Mistakes

Common Hook Mistakes

React Hooks Fundamentals

Common Hook Mistakes in React JS

React Hooks are special functions that allow functional components to use state, effects, context, references, and other React features. Common Hook mistakes occur when developers call Hooks incorrectly, manage state improperly, use incorrect dependencies, or create unnecessary effects. Understanding these mistakes helps developers write clean, reliable, reusable, and maintainable React applications.

Course: JustAcademy React JS Training Course

Course Demo: Register for React JS Course Demo


1. What Are React Hooks?

React Hooks are functions that let functional components use React features such as state, effects, context, and references without writing class components.

Commonly used React Hooks include:

  • useState – manages component state.
  • useEffect – synchronizes a component with external systems.
  • useContext – reads values from React context.
  • useRef – stores a reference or mutable value that does not trigger a re-render when changed.
  • useMemo – caches a calculation result between renders.
  • useCallback – caches a function definition between renders.
  • useReducer – manages state using a reducer function.

Each Hook has its own purpose and rules. Using Hooks correctly helps prevent unexpected behavior and makes components easier to maintain.


2. Calling Hooks Inside Conditions

Mistake: Calling a Hook inside an if statement, conditional expression, or another conditional block.

React relies on Hooks being called in the same order during each render. Calling a Hook conditionally can change that order and cause errors.

Incorrect Example

import { useState } from "react";
function UserProfile({ isLoggedIn }) {
  if (isLoggedIn) {
    const [name, setName] = useState("Amit");
  }
  return <h2>User Profile</h2>;
}

Problem: The Hook is called only when isLoggedIn is true. When the condition changes, the order of Hook calls may become inconsistent.

Correct Example

import { useState } from "react";
function UserProfile({ isLoggedIn }) {
  const [name, setName] = useState("Amit");
  if (!isLoggedIn) {
    return <p>Please log in.</p>;
  }
  return <h2>Welcome, {name}!</h2>;
}

Best practice: Call Hooks at the top level of a function component or custom Hook, before conditional returns.


3. Calling Hooks Inside Loops

Mistake: Calling Hooks inside for, while, or other loops.

Incorrect Example

function ProductList({ products }) {
  for (const product of products) {
    const [selected, setSelected] = useState(false);
  }
  return <h2>Product List</h2>;
}

Problem: The number of Hook calls may change when the number of products changes.

Correct Example

import { useState } from "react";
function ProductItem({ product }) {
  const [selected, setSelected] = useState(false);
  return (
    <button onClick={() => setSelected(!selected)}>
      {product.name}: {selected ? "Selected" : "Select"}
    </button>
  );
}
function ProductList({ products }) {
  return (
    <div>
      {products.map(product => (
        <ProductItem key={product.id} product={product} />
      ))}
    </div>
  );
}

Explanation: Each product has its own component and state. The Hook is called at the top level of ProductItem, not inside the loop.


4. Calling Hooks Inside Event Handlers

Mistake: Calling a Hook inside a button click handler or another event handler.

Incorrect Example

function Counter() {
  function handleClick() {
    const [count, setCount] = useState(0);
    setCount(count + 1);
  }
  return <button onClick={handleClick}>Increase</button>;
}

Problem: Event handlers run in response to user actions, not as part of rendering a React component. Hooks cannot be called inside them.

Correct Example

import { useState } from "react";
function Counter() {
  const [count, setCount] = useState(0);
  function handleClick() {
    setCount(currentCount => currentCount + 1);
  }
  return (
    <button onClick={handleClick}>
      Count: {count}
    </button>
  );
}

Best practice: Declare Hooks at the top level and use event handlers to update state or perform actions.


5. Updating State Directly

Mistake: Modifying a state variable directly instead of using its setter function.

Incorrect Example

function Counter() {
  const [count, setCount] = useState(0);
  function increaseCount() {
    count = count + 1;
  }
  return <button onClick={increaseCount}>{count}</button>;
}

Problem: State is a snapshot for a particular render. Directly changing the variable does not tell React to update the component and is not a valid way to manage state.

Correct Example

import { useState } from "react";
function Counter() {
  const [count, setCount] = useState(0);
  function increaseCount() {
    setCount(count + 1);
  }
  return (
    <button onClick={increaseCount}>
      Count: {count}
    </button>
  );
}

Best practice: Use the setter function returned by useState to update state.


6. Ignoring the Previous State When Updating

Mistake: Using a state value directly when the next value depends on the previous value, especially when several updates are queued together.

Potentially Incorrect Example

function Counter() {
  const [count, setCount] = useState(0);
  function increaseThreeTimes() {
    setCount(count + 1);
    setCount(count + 1);
    setCount(count + 1);
  }
  return (
    <button onClick={increaseThreeTimes}>
      Count: {count}
    </button>
  );
}

Problem: Each update uses the same value captured by the current render. The result may increase by only one instead of three.

Correct Example

function increaseThreeTimes() {
  setCount(currentCount => currentCount + 1);
  setCount(currentCount => currentCount + 1);
  setCount(currentCount => currentCount + 1);
}

Explanation: Functional state updates receive the latest queued state value, so each update builds on the previous update.


7. Forgetting Dependencies in useEffect

Mistake: Leaving out reactive values used inside a useEffect callback.

Incorrect Example

import { useEffect, useState } from "react";
function UserGreeting() {
  const [name, setName] = useState("Amit");
  useEffect(() => {
    console.log("Hello", name);
  }, []);
  return (
    <button onClick={() => setName("Rahul")}>
      Change Name
    </button>
  );
}

Problem: The effect does not rerun when name changes, because the dependency array is empty.

Correct Example

useEffect(() => {
  console.log("Hello", name);
}, [name]);

Best practice: Include all reactive values used by an effect. Dependencies should match the code rather than being chosen simply to control how often the effect runs.


8. Creating Infinite Loops with useEffect

Mistake: Updating state inside an effect in a way that continually changes one of its dependencies.

Incorrect Example

import { useEffect, useState } from "react";
function Counter() {
  const [count, setCount] = useState(0);
  useEffect(() => {
    setCount(count + 1);
  }, [count]);
  return <p>Count: {count}</p>;
}

Problem: The effect changes count. The state update causes another render, which changes the dependency and triggers the effect again.

Correct Approach

function Counter() {
  const [count, setCount] = useState(0);
  return (
    <button onClick={() => setCount(value => value + 1)}>
      Count: {count}
    </button>
  );
}

Best practice: Use event handlers for actions caused by user interaction. Before adding state updates to an effect, check whether the effect is necessary and whether it is synchronizing with an external system.


9. Forgetting Cleanup in useEffect

Mistake: Starting a timer, subscription, or event listener without cleaning it up when it is no longer needed.

Incorrect Example

useEffect(() => {
  const timerId = setInterval(() => {
    console.log("Timer running");
  }, 1000);
}, []);

Problem: The timer continues running after the component is removed unless it is cleared.

Correct Example

useEffect(() => {
  const timerId = setInterval(() => {
    console.log("Timer running");
  }, 1000);
  return () => {
    clearInterval(timerId);
  };
}, []);

Explanation: The cleanup function stops the timer when the effect is cleaned up, including when the component unmounts. React also runs cleanup before setting up the effect again when its dependencies change.


10. Using useEffect for Every Calculation

Mistake: Using an effect and extra state to calculate a value that can be derived directly from existing props or state.

Unnecessary Example

function ProductTotal({ price, quantity }) {
  const [total, setTotal] = useState(0);
  useEffect(() => {
    setTotal(price * quantity);
  }, [price, quantity]);
  return <p>Total: ₹{total}</p>;
}

Problem: The total is calculated from values that are already available. Storing it separately can introduce an unnecessary render and duplicate state.

Better Example

function ProductTotal({ price, quantity }) {
  const total = price * quantity;
  return <p>Total: ₹{total}</p>;
}

Best practice: Calculate derived values during rendering. Use useMemo only when caching an expensive calculation is useful.


11. Using useMemo and useCallback Unnecessarily

Mistake: Adding useMemo or useCallback to every value or function without a clear performance reason.

Example

const fullName = useMemo(() => {
  return firstName + " " + lastName;
}, [firstName, lastName]);

For a simple string concatenation, ordinary calculation is usually sufficient.

Simpler Approach

const fullName = firstName + " " + lastName;

Best practice: Start with simple code. Use memoization when profiling or component behavior shows that caching a calculation or function reference provides a real benefit.


12. Mutating Objects or Arrays in State

Mistake: Changing an existing object or array directly and then passing the same reference to the state setter.

Incorrect Example

function TodoList() {
  const [todos, setTodos] = useState(["Learn React"]);
  function addTodo() {
    todos.push("Learn Hooks");
    setTodos(todos);
  }
  return (
    <button onClick={addTodo}>
      Add Todo
    </button>
  );
}

Problem: The original array is mutated, and the same reference is passed back. React may not detect a changed state value as expected.

Correct Example

function addTodo() {
  setTodos(currentTodos => [
    ...currentTodos,
    "Learn Hooks"
  ]);
}

Best practice: Treat state objects and arrays as immutable. Create a new object or array when updating them.


13. Calling Hooks Outside React Components

Mistake: Calling Hooks inside ordinary JavaScript functions that are neither React function components nor custom Hooks.

Incorrect Example

function getUserName() {
  const [name, setName] = useState("Amit");
  return name;
}

Problem: This is a regular JavaScript function, so React cannot treat the Hook call as part of a component's rendering.

Correct Example: Custom Hook

import { useState } from "react";
function useUserName() {
  const [name, setName] = useState("Amit");
  return { name, setName };
}
function UserProfile() {
  const { name, setName } = useUserName();
  return (
    <div>
      <p>Name: {name}</p>
      <button onClick={() => setName("Rahul")}>
        Change Name
      </button>
    </div>
  );
}

Best practice: Call Hooks only from React function components or custom Hooks. Custom Hook names should begin with use.


14. Using Hooks After an Early Return

Mistake: Returning from a component before all its regular Hooks have been called.

Incorrect Example

function Profile({ user }) {
  if (!user) {
    return <p>No user found</p>;
  }
  const [active, setActive] = useState(true);
  return <p>{active ? user.name : "Inactive"}</p>;
}

Problem: When user is missing, the component returns before calling useState. This can change the order of Hook calls between renders.

Correct Example

function Profile({ user }) {
  const [active, setActive] = useState(true);
  if (!user) {
    return <p>No user found</p>;
  }
  return <p>{active ? user.name : "Inactive"}</p>;
}

Best practice: Call all regular Hooks before conditional returns.


15. Ignoring React Strict Mode Behavior

Mistake: Assuming an effect will always run only once during development when the dependency array is empty.

When React Strict Mode is enabled, React may run an additional effect setup-and-cleanup cycle in development to help identify missing cleanup logic. This is intentional behavior and does not mean that React will perform the same extra cycle in production.

Example

useEffect(() => {
  console.log("Effect started");
  return () => {
    console.log("Effect cleaned up");
  };
}, []);

Best practice: Write effects with correct setup and cleanup behavior. Do not disable Strict Mode just to hide an effect-related bug.


16. Incorrectly Using useRef

Mistake: Expecting a change to ref.current to trigger a component re-render.

Example

const countRef = useRef(0);
function increaseCount() {
  countRef.current += 1;
}

Changing countRef.current does not automatically update the UI. A ref is suitable for values that need to persist between renders without triggering a re-render.

Correct Approach for Displayed State

const [count, setCount] = useState(0);
function increaseCount() {
  setCount(currentCount => currentCount + 1);
}

Best practice: Use useState for values that affect displayed UI and useRef for references or mutable values that do not need to trigger rendering.


17. Forgetting Keys When Rendering Lists

Mistake: Rendering lists without a stable, unique key for each item.

Incorrect Example

{products.map(product => (
  <li>{product.name}</li>
))}

Correct Example

{products.map(product => (
  <li key={product.id}>
    {product.name}
  </li>
))}

Explanation: Keys help React identify list items when they are added, removed, or reordered. Missing or unstable keys can cause warnings and incorrect component-state associations. Use a stable unique ID whenever possible.


18. Common Hook Mistakes Summary

Common Mistake Why It Is a Problem Solution
Calling Hooks conditionally Changes Hook call order. Call Hooks at the top level.
Calling Hooks inside loops Hook call count may change. Move state into separate components or custom Hooks.
Calling Hooks in event handlers Event handlers are not component rendering. Declare Hooks at the component's top level.
Direct state mutation React may not detect updates correctly. Use setters and immutable updates.
Missing dependencies Effects can use outdated values. Include reactive dependencies.
Infinite effect loops Repeated state changes trigger repeated effects. Review effect purpose and state updates.
Missing cleanup Timers or subscriptions may continue unnecessarily. Return a cleanup function when required.
Unnecessary effects Creates extra complexity and possibly extra renders. Calculate derived values directly.
Unnecessary memoization Makes code harder to maintain without clear benefit. Memoize only when useful.
Incorrect useRef expectations Ref changes do not trigger re-renders. Use state for values displayed in the UI.

19. Best Practices for React Hooks

  • Follow the Rules of Hooks and call regular Hooks only at the top level of React function components or custom Hooks.
  • Use meaningful names for state variables and custom Hooks.
  • Update state through setter functions.
  • Use functional state updates when the next value depends on the previous value.
  • Include all required reactive values in effect dependency arrays.
  • Use cleanup functions for timers, subscriptions, event listeners, and connections when needed.
  • Avoid using effects for calculations that can be performed during rendering.
  • Use useMemo and useCallback when they offer a practical benefit.
  • Keep state objects and arrays immutable.
  • Use the React Hooks lint rules to identify common mistakes.
  • Test components with React Strict Mode enabled during development.

20. Practical Project: Counter Using React Hooks

Create a counter application that demonstrates correct use of useState, event handlers, and functional state updates.

Requirements

  • Display the current count.
  • Provide an Increase button.
  • Provide a Decrease button.
  • Provide a Reset button.
  • Use the state setter to update the count.
  • Avoid unnecessary effects.

Complete Code

import { useState } from "react";
function CounterApp() {
  const [count, setCount] = useState(0);
  const increase = () => {
    setCount(current => current + 1);
  };
  const decrease = () => {
    setCount(current => current - 1);
  };
  const reset = () => {
    setCount(0);
  };
  return (
    <div>
      <h1>React Counter</h1>
      <p>Current Count: {count}</p>
      <button onClick={increase}>Increase</button>
      <button onClick={decrease}>Decrease</button>
      <button onClick={reset}>Reset</button>
    </div>
  );
}
export default CounterApp;

Learning outcome: This project demonstrates how to declare a Hook correctly, update state without direct mutation, and handle user interactions without unnecessary effects.


21. React Hooks Interview Questions and Answers

Q1. What are React Hooks?

Hooks are functions that allow functional components to use React features such as state, effects, context, and references.

Q2. What are the Rules of Hooks?

Regular Hooks must be called at the top level of React function components or custom Hooks, not inside loops, conditions, or nested functions.

Q3. Why should we not call Hooks conditionally?

React relies on a consistent order of Hook calls to associate state and other Hook data with the correct component logic.

Q4. Why should we not mutate state directly?

State is an immutable snapshot for each render. Direct mutation can prevent React from detecting updates as expected and can cause inconsistent UI behavior.

Q5. What is a stale closure in React?

A stale closure occurs when a callback or effect uses values captured from an earlier render when newer values are expected.

Q6. What is the purpose of the useEffect dependency array?

It identifies the reactive values that determine when an effect should synchronize again.

Q7. Why can useEffect cause an infinite loop?

An effect can loop when it updates state and that update changes one of the effect's dependencies, triggering another run.

Q8. Why is cleanup important in useEffect?

Cleanup releases external resources such as timers, subscriptions, event listeners, and connections when they are no longer needed.

Q9. What is the difference between useState and useRef?

useState stores data that can trigger a re-render when updated. useRef stores a persistent mutable reference whose changes do not trigger a re-render.

Q10. When should useMemo be used?

useMemo can cache an expensive calculation when its dependencies remain unchanged. It is a performance optimization and is not necessary for every calculation.

Q11. Can Hooks be used inside custom Hooks?

Yes. Custom Hooks can call other Hooks, provided they follow the Rules of Hooks.

Q12. How can developers prevent common Hook mistakes?

Follow the Rules of Hooks, update state immutably, declare correct dependencies, clean up external resources, and use the React Hooks lint rules.


22. Practice Exercises

  1. Create a counter using useState without directly modifying the state variable.
  2. Write a component that calls Hooks correctly when a user is logged in or logged out.
  3. Render a list of products using separate components and stable keys.
  4. Fix an effect that uses state but has an empty dependency array.
  5. Find and correct an infinite loop caused by an effect updating its own dependency.
  6. Create a timer with a cleanup function.
  7. Calculate a product total directly from price and quantity without unnecessary state.
  8. Create a custom Hook that manages a username.
  9. Compare useState and useRef in a small interactive application.
  10. Enable React Strict Mode and verify that effects handle setup and cleanup correctly.

23. Quick Revision Checklist

  • Understand the purpose of React Hooks.
  • Call Hooks at the top level of components or custom Hooks.
  • Avoid calling Hooks inside conditions, loops, or event handlers.
  • Use state setter functions instead of direct mutation.
  • Use functional updates when the next state depends on the previous state.
  • Declare correct effect dependencies.
  • Prevent unnecessary effect loops.
  • Clean up timers, subscriptions, and event listeners.
  • Use state for values that affect displayed UI.
  • Use memoization only when it is beneficial.
  • Follow lint recommendations and test your components.

24. Conclusion

Common Hook mistakes can lead to incorrect state updates, stale values, infinite loops, memory leaks, and unexpected rendering behavior. Developers can avoid these problems by following the Rules of Hooks, updating state correctly, declaring dependencies accurately, and cleaning up external resources. Writing simple components and using effects only when needed makes React applications easier to understand, debug, and maintain.

Official React Resources: Rules of Hooks | Removing Effect Dependencies | Components and Hooks Must Be Pure

Explore React JS Training: JustAcademy React JS Training Course | Register for React JS Course Demo

whatsapp