Skip to content

Common Mistakes with useState hooks

Posted on:June 18, 2023

In this article, we’ll delve into a few common mistakes that developers, both beginners and advanced, frequently make when using the useState and useEffect hooks.

Table of contents

Open Table of contents

1.Using State When You Don’t Need It

One of the most prevalent mistakes is using state unnecessarily. Consider a scenario where you have a form with email and password fields. The mistake arises when developers use state to track the values of these fields, even though they only need the values when the form is submitted.

const MyForm = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");

  const handleSubmit = () => {
    console.log(email, password);
    // Additional logic, e.g., making a fetch request
  };

  return (
    <form onSubmit={handleSubmit}>
      <input
        type="email"
        value={email}
        onChange={e => setEmail(e.target.value)}
      />
      <input
        type="password"
        value={password}
        onChange={e => setPassword(e.target.value)}
      />
      <button type="submit">Submit</button>
    </form>
  );
};

In this case, we are using state for email and password, but the values are only needed when the form is submitted. We can optimize this by using useRef instead of state.

import { useRef } from "react";

const MyForm = () => {
  const emailRef = useRef("");
  const passwordRef = useRef("");

  const handleSubmit = () => {
    console.log(emailRef.current.value, passwordRef.current.value);
    // Additional logic, e.g., making a fetch request
  };

  return (
    <form onSubmit={handleSubmit}>
      <input type="email" ref={emailRef} />
      <input type="password" ref={passwordRef} />
      <button type="submit">Submit</button>
    </form>
  );
};

By using useRef, we avoid unnecessary re-renders caused by state updates every time the user types a character. This approach is especially beneficial for form inputs where the value is only required upon submission.

2. Overusing State Variables

Another mistake is overusing state variables, leading to unnecessary re-renders. Consider a component that has multiple state variables, and you’re updating all of them frequently.

const MyComponent = () => {
  const [name, setName] = useState("");
  const [age, setAge] = useState(0);
  const [city, setCity] = useState("");

  const handleNameChange = e => {
    setName(e.target.value);
  };

  const handleAgeChange = e => {
    setAge(Number(e.target.value));
  };

  const handleCityChange = e => {
    setCity(e.target.value);
  };

  // Component rendering logic...
};

In this example, every time a user types in any input field, all three state variables (name, age, and city) trigger re-renders. This is inefficient, especially when the values are unrelated.

To solve this, you can use a single state object.

const MyComponent = () => {
  const [userData, setUserData] = useState({
    name: "",
    age: 0,
    city: "",
  });

  const handleInputChange = (e, field) => {
    setUserData({ ...userData, [field]: e.target.value });
  };

  // Component rendering logic...
};

Now, a single state update is triggered, and only the specific field being modified causes a re-render.

3. Incorrectly Updating State Based on Previous State

When updating state based on its previous value, it’s crucial to use the functional form of the state updater to avoid race conditions and ensure accuracy. Consider the following example.

const Counter = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(count + 1); // Incorrect
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

The above code might not work as expected because the count value might not be updated immediately due to React’s asynchronous state updates. To ensure accuracy, use the functional form of the state updater.

const Counter = () => {
  const [count, setCount] = useState(0);

  const increment = () => {
    setCount(prevCount => prevCount + 1); // Correct
  };

  return (
    <div>
      <p>Count: {count}</p>
      <button onClick={increment}>Increment</button>
    </div>
  );
};

Conclusion

In conclusion, mastering the useState hook involves not just understanding its syntax but also using it judiciously to avoid unnecessary re-renders and improve the overall performance of your React applications.