React Hooks Common Mistakes
Tags: javascript, react, beginners, webdev
Published: 2020-11-19 11:11:30

---
title: React Hooks Common Mistakes
published: true
canonical_url: https://bugfender.com/blog/react-hooks-common-mistakes/
---
![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/ciwelcohguf27u3s3gd8.jpg)
**This article originally appeared at [bugfender.com](https://bugfender.com):**  [react-hooks-common-mistakes](https://bugfender.com/blog/react-hooks-common-mistakes/).

React Hooks is a new addition to React which enables you to use state and other features of the library without having to create a class.

By 'hooking into' React's existing features, you can significantly reduce the number of concepts you have to grapple with, and you can create custom hooks to share all kinds of non-visual logic, making your code much more reusable. React Hooks are engineered to mesh with your existing codebase, so you don't need to go back and rip up the code you've already written. 

This promises to be a huge step forward for the dev community, and this post will help you maximize the opportunity by flagging a number of common mistakes. I've made most (if not all) of these errors myself, so I'll be talking with the benefit of experience! 

## First of All, What Is The Difference Between React Hooks and Class Components?

Let's look at some of the key variations between React Hooks and the Class Components typically used to write React code. 

The good news is that most of the basic concepts are still the same. However, there are some essential differences to bear in mind - and all of them are beneficial.

1.  **In React Hooks, all the components are functions.** So we don't need `this` and there's no need to deal with all the "bind" issues.
2. **Easier lifecycle.** In React Hooks, we don't need to memorize the entire react lifecycle and manage it in our code. In fact most of the lifecycle can be managed from `useEffect` (and some less common methods like `useLayoutEffect`).
3. **States.** In classes, we initialized the state via `this.state = {...}` and needed to apply `setState` any time we wanted to update it. With React Hooks, we can separate the `state` with `useState` and update individual parts of it.

# Common Mistakes With useEffect

Ok, so now let's get into the nitty-gritty and look at some of the common mistakes that I (and other devs) have made. We'll begin by looking at useEffect.

To give this a little extra edge, try to guess the error before continuing reading. This way, you can test your React Hooks knowledge.

**To kick things off, here is some basic code:** 

```jsx
import React, {useEffect, useState} from "react";
import "./styles.css";

export default function App() {
  const [arr, setArr] = useState([]);
  useEffect(()=>{
    setArr([1])
  })
  return (
    <div className="App">
    </div>
  );
}
```

As you've hopefully spotted, the code reaches `Uncaught RangeError: Maximum call stack size exceeded` and cause an infinity loop. 

This happens because of the **missing `useEffect` dependency**. This dependency "tells" the `useEffect` to run the function if the dependency is changed (like `shouldComponentUpdate`). 

Without the dependency, `useEffect` will be active after any render (when we do `setArr`, we cause a re-render). 

L**et's add an `arr` dependency and see what happens:** 

```jsx
import React, {useEffect, useState} from "react";
import "./styles.css";

export default function App() {
  const [arr, setArr] = useState([]);
  useEffect(()=>{
    setArr([1])
  },[arr])
  return (
    <div className="App">
    </div>
  );
}
```

Despite the new addition, the code still reaches `Uncaught RangeError: Maximum call stack size exceeded`. 

This type of error is pretty hard to understand. If we run the code in this way (and remember, this is just for explanation)...

```jsx
useEffect(()=>{
    setArr(arr)
  },[arr])
}
```

...we don't cause an infinity loop. 

How can we explain the difference?

Actually, it's all because Arrays in JavaScript are references. So when we do `setArr([1])` it's the same as `arr = [1]`. 

In this way, we change the reference any time we perform `useEffect(()=>{setArr([1])},[arr])` and cause an infinity loop.

**Now, let's look at a more extensive codebase:**

```jsx
import React, { useEffect, useState, useCallback } from "react";
import "./styles.css";

function List(props) {
  const [arr, setArr] = useState([]);
  useEffect(() => {
    setArr((previousArray) => [...previousArray, props.num + 1]);
  }, []);
  return <button>Click me</button>;
}

export default function App() {
  const [num, setNum] = useState(1);
  const addToNum = () => setNum((previousNum) => previousNum + 1);
  return (
    <div className="App">
      <button onClick={addToNum}>add num</button>
      <List num={num}></List>
    </div>
  );
}
```

To understand this code better, we can break it down into its individual parts.

- We have two components: `List` , which receives props, and `App`.
- App has a button when you click on it, which adds + 1 to `num`.
- App also has a child component, `List` , which sends the `num` as props.
- The `List` (for now), adds + 1 to the `props.num` and pushes it to the `arr` in the first render (empty dependency).

This code works fine. The arr will be `[2]`.  But we get a warning: `React Hook useEffect has a missing dependency: props.num. Either include it or remove the dependency array.` 

Everything works fine, and nothing is stuck, so why are we getting this warning? 

In fact we have to thank the brilliant React engineers here, because they have already flagged this for us.

The `props.num` doesn't exist as a dependency. This means that any time we click `add num`, we render the child component without adding the new value to `arr`. 

Here's a solution:

```jsx
useEffect(() => {
    setArr((previousArray) => [...previousArray, props.num + 1]);
  }, [props.num]);
```

This way, the `arr` will change when the `num` changes.

**But what about the List function?**

Specifically, what happens if we want to give `List` the ability to add to `arr` from the button (element) as well? Well we need to do something like this:

```jsx
function List(props) {
  const [arr, setArr] = useState([]);
  const addToArr = () => setArr((previousArray) => [...previousArray, props.num + 1]);
  useEffect(() => {
    addToArr();
  }, [props.num]);
  console.log(arr);
  return <button onClick={addToArr}>Add to array</button>;
}
```

The code works fine (it does what we want), but now we see the warning: `React Hook useEffect has a missing dependency: 'addToArr'. Either include it or remove the dependency array`. 

In fact, if we add the `addToArr` to the dependency list, it will cause an infinity loop (I believe it's  self-invoked and the result of the function is different on any render. if you know the reason, please add a comment below).

The solution is to add a `useCallback` :

```jsx
function List(props) {
  const [arr, setArr] = useState([]);
  const addToArr = useCallback(() => {
    setArr((previousArray) => [...previousArray, props.num + 1]);
  }, [props.num]);
  useEffect(() => {
    addToArr();
  }, [addToArr]);
  console.log(arr);
  return <button onClick={addToArr}>Add to array</button>;
}
```

`useCallback` memorizes the function, and in this way we can use the `addToArr` dependency without a problem. 

If you want to read more about this, here's good explanation from StackOverflow: [about-infinite-loop-in-useeffect](https://stackoverflow.com/a/59236743/10055871).

## One Final Mistake...

Let's say you have an application with users and admins. There is only one API that gives you all the app data (`fetchDummyData`), and you need to separate it into two different states (`users`, `admins`).

Try to spot what is wrong here:

```jsx
import React, { useEffect, useState } from "react";
import "./styles.css";

function App() {
  const [users, setUsers] = useState([]);
  const [admins, setAdmins] = useState([]);

  const fetchDummyData = () =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve([
          { name: "Tomer", type: "user" },
          { name: "John", type: "admin" },
          { name: "Dan", type: "user" }
        ]);
      }, 300);
    });

  const findUsers = (data) =>
setUsers(() => data.filter((row) => row.type === "user"));

  const findAdmins = (data) =>
    setAdmins(() => data.filter((row) => row.type === "admin"));

  useEffect(() => {
    const promiseData = fetchDummyData();
    promiseData.then((data) => {
      findUsers(data);
      findAdmins(data);
    });
  }, []);
  console.count("render");
  return (
    <div className="App">
      <Users users={users}></Users >
      <Admins admins={admins}></Admins >
    </div>
  );
}
export default App;
```

As you probably noticed from the `console.count("render")`, there is something wrong with the number of renders. 

According to basic logic, we need to see `render: 2`  the first time the component mounts, and then a re-render after `useEffect`. But in fact we see `render : 3`.

This is because any time we deploy the useState method, the component re-renders (`setUsers` , `setAdmins`).

NB: If you are using `React.StrictMode` in `index.js`, it will re-render twice. This means you will see the result of `console.count` in multiply 2 ("render:6"). for more information [https://reactjs.org/docs/strict-mode](https://reactjs.org/docs/strict-mode)

The solution, in this case, is to use a state like this:

```jsx
function App() {
  const [allUsers, setAllUsers] = useState({ users: [], admins: [] });

  const fetchDummyData = () =>
    new Promise((resolve, reject) => {
      setTimeout(() => {
        resolve([
          { name: "Tomer", type: "user" },
          { name: "John", type: "admin" },
          { name: "Dan", type: "user" }
        ]);
      }, 300);
    });
  const findUsers = (data) => data.filter((row) => row.type === "user");
  const findAdmins = (data) => data.filter((row) => row.type === "admin");
  useEffect(() => {
    const promiseData = fetchDummyData();
    promiseData.then((data) => {
      setAllUsers({
        users: findUsers(data),
        admins: findAdmins(data)
      });
    });
  }, []);
  console.count("render");
  return (
    <div className="App">
      <Users users={allUsers.users}></Users >
      <Admins admins={allUsers.admins}></Admins >
    </div>
  );
}

export default App;
```

This way we cause the opponent to re-render only once, because we set the state only once. If you have a very complex state, maybe the better solution is to apply `useReducer`.

# Remember: Bugfender Can Help

Bugfender can help you to find errors in your apps. There may be some errors that you cannot see in development and only happen in production, to certain users.

Once you install Bugfender in your app, we will be notified of any problem that your app users experience.

Thank you for reading. I hope you enjoyed the tutorial and learned something new. If you have something to add, please leave a comment.