Avoiding Common useState() Mistakes in React
Tags: react, usestate, dev, javascript
Published: 2024-07-01 08:46:11

---
title: Avoiding Common useState() Mistakes in React
published: true
description: Steer clear of common pitfalls with React's useState hook!
tags: react, useState, dev, javascript
cover_image: https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6vcu60ntg7g114a84anc.png
# Use a ratio of 100:42 for best results.
# published_at: 2024-07-01 06:31 +0000
---


React's `useState` hook is a powerful tool for managing component state, but even the most seasoned developers can fall prey to common pitfalls. 

Here, we'll explore these mistakes and equip you with the knowledge to craft robust and performant React applications.

**1. Mutating State Directly - A Cardinal Sin**

React relies on immutability for state updates. This means you should never directly modify the state object returned by `useState`. Instead, use the setter function provided to create a new state object.

**Avoid This ❌**

```javascript
const [count, setCount] = useState(0);

const handleClick = () => {
  count++; // Wrong! This mutates state directly
}
```


**Do This ✅**

```javascript
const handleClick = () => {
  setCount(count + 1); // Create a new state object with updated value
}
```


**2. Forgetting Prior State - Keeping Track of Changes**

When updating `state`, it's crucial to consider the previous state value. React updates occur asynchronously, so directly referencing the current state within the update function might lead to unexpected behavior.

**Avoid This ❌**

```javascript
const [todos, setTodos] = useState([]);

const handleAddTodo = (text) => {
  // This might miss newly added todos if called rapidly
  setTodos([...todos, text]); 
}
```

**Do This ✅**

```javascript
const handleAddTodo = (text) => {
  setTodos((prevTodos) => [...prevTodos, text]); // Use the previous state
}
```



**3. Overusing State for Derived Values - When Less is More**

`useState` is ideal for managing simple state, but for complex derived values, consider alternatives like `useMemo` or custom memoization functions. Overusing state for derived values can lead to unnecessary re-renders and performance issues.

**Avoid This ❌**

```javascript
const [todos, setTodos] = useState([]);

const completedTodos = todos.filter((todo) => todo.completed); // Derived value

const renderTodos = () => {
  return (
    <ul>
      {completedTodos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};
```

If a new todo is added (`setTodos([...todos, newTodo])`), even if it's not completed, both `todos` and `completedTodos` will be updated, triggering a re-render of the `renderTodos` function, even though only the incomplete list needs to be updated.

This unnecessary re-render can be avoided by using techniques like `useMemo`.

**Do This ✅**

```javascript
import { useMemo } from 'react';

const [todos, setTodos] = useState([]);

const completedTodos = useMemo(() => todos.filter((todo) => todo.completed), [todos]); // Memoize based on todos

const renderTodos = () => {
  return (
    <ul>
      {completedTodos.map((todo) => (
        <li key={todo.id}>{todo.text}</li>
      ))}
    </ul>
  );
};
```

`useMemo` ensures `completedTodos` is only recalculated when the `todos` array actually changes (based on the dependency array `[todos]`).


**4. Neglecting Optional Chaining - Avoiding Nullish Errors**

When dealing with potential null or undefined values within your state object, leverage optional chaining (?.) to prevent errors. This ensures the graceful handling of missing data.


**Avoid This ❌**

```javascript

const user = { name: "John" };
const [currentUser, setCurrentUser] = useState(user);

const displayUserName = () => {
  return currentUser.name; // Might throw an error if currentUser is null
}
```

**Do This ✅**

```javascript
const displayUserName = () => {
  return currentUser?.name; // Safe access using optional chaining
}
```



**5. Managing Multiple Inputs - A Formidable Challenge**

Handling forms with multiple input fields can get tricky. Consider using an object or an array within your state to manage individual field values. This allows for easier updates and avoids creating separate state variables for each field.

**Avoid This ❌**

```javascript
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [message, setMessage] = useState('');

const handleChange = (event) => {
  if (event.target.name === 'name') {
    setName(event.target.value);
  } else if (event.target.name === 'email') {
    setEmail(event.target.value);
  } else if (event.target.name === 'message') {
    setMessage(setMessage);
  }
};
```

* **Code Repetition:** Updating each field requires separate logic within the handleChange function, making the code repetitive and error-prone.
* **State Management Complexity:** As the number of fields increases, managing numerous state variables and their corresponding setters becomes cumbersome.

**Do This ✅**

```javascript
const MyForm = () => {
  const [formData, setFormData] = useState({ name: '', email: '', message: '' });

  const handleChange = (event) => {
    setFormData({ ...formData, [event.target.name]: event.target.value });
  };

  return (
    <form onSubmit={(e) => e.preventDefault()}>
      <label htmlFor="name">Name:</label>
      <input type="text" id="name" name="name" value={formData.name} onChange={handleChange} />
      <label htmlFor="email">Email:</label>
      <input type="email" id="email" name="email" value={formData.email} onChange={handleChange} />
      <label htmlFor="message">Message:</label>
      <textarea id="message" name="message" value={formData.message} onChange={handleChange} />
      <button type="submit">Submit</button>
    </form>
  );
};

```

* **Concise State Management:** All form data is stored in a single place, simplifying state management and updates.
* **Cleaner Update Logic:** The `handleChange` function becomes more concise, using the spread operator (`...formData`) to update a specific field within the state object.



**Remember:** When dealing with complex forms with nested structures or conditional logic, consider using libraries like [Formik](https://formik.org) or [React Hook Form](https://react-hook-form.com/get-started) to streamline form validation and state management.



> **Bonus Tip:** When in Doubt, Use [useReducer](https://react.dev/reference/react/useReducer)

For complex state management scenarios with intricate update logic, explore `useReducer`. It provides a more predictable way to handle state updates, especially when dealing with nested state structures.

By following these guidelines and adopting best practices, you'll write cleaner, more maintainable React components that effectively leverage the power of `useState`. Remember, a well-managed state is the heart of a responsive and performant React application.

**Want to stay up-to-date on the latest Tips and Tricks? Like this post and follow us for more content like this!**

Happy Coding!!!