useEffect Hook - Fetching Data With useEffect
useEffect Hook - Fetching Data With useEffect

Hello and welcome back to the channel. In this video you will learn useEffect hook. This is the most complicated but versatile hook in react hooks and I will share a lot of advanced stuff so make sure that you stay until the end of the video.
Let's jump right into it.

Here I have empty create-react-app project where we can directly start writing code. If you don't know how to generate react project with create-react-app I made a video on that so go check it out first.

I you can see in browser I have just a single h1 tag and it's our App.js component. Let's have a look. So it's a normal stateless React component with markup inside.

As we are using React bigger than 16 version (actually 17) we can use hooks inside without installing any additional packages.

So what do we need useEffect for? As you can understand from title it is to make side effects in components. What are side effects? Fetch data from API, working directly with DOM, communicating via web sockets, writing to localStorage. This are all side effects.

So we already learned what is useState hook. If you didn't watch that video go watch it first. Now let's create a counter example and every time when we change our counter we change the title of the page.

import React, { useState, useEffect } from "react";

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

  useEffect(() => {
    document.title = `You clicked ${count} times`;
  });

  return (
    <div>
      <p>You clicked {count} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
    </div>
  );
}
export default App;

So as you can see we just call useEffect directly inside our component. Inside we are passing a callback what will be done. In this case we are setting a title of the page.

If we are comparing classes to react hooks useEffect is the combination of componentDidMount, componentDidUpdate, componentWillUnmount.

Now as you can see in browser, our title is updated every time when we click. So how exactly is it working? When we change a state with setter (in our case setCount) our component is rerendered. Let's write the console inside component at the beginning and inside useEffect.

function App() {
  console.log("component is rerendered");
  ...

  useEffect(() => {
    console.log("use effect is triggered");
    ...
  });

  ...
}

As you can see every time when we change the state we get 2 logs. And order is extremely important to understand the concept of useEffect. First of all we get the console that component is rerendered and only after that useEffect was triggered. So useEffect happens always after component was rerendered.

Now the first question why do we have such strange notation that we write useEffect inside component directly? This is the easiest way to give useEffect direct access to all local variables without any additional code. And this is the only correct place to write useEffect. Only directly inside component, without any nesting or if conditions. No useEffect in closures or inner functions. Just directly in component. In all other cases you will get only problems.

You next question is why do we need use effect if it just runs after every rerender? Why not just write some code inside a component. There are 2 reasons for this. First of all only with useEffect you are sure that React finished rerendering of the component. Second reason is that it's possible to run useEffect not on every rerender but when we need. For example when some property changed


We can do that by providing the list of dependencies as a second argument. Then our effect will only trigger when our dependency was changed.

  useEffect(() => {
    console.log("use effect is triggered");
    document.title = `You clicked ${count} times`;
  }, [count]);

So here we provided our count state as a dependency. So we are saying that we need to call this effect only when count property changes.

In our case this code will work exactly like before because the only state that changes in our application is count. Let's create 1 more counter to see the difference.

function App() {
  console.log("component is rerendered");
  const [count, setCount] = useState(0);
  const [count2, setCount2] = useState(0);

  useEffect(() => {
    console.log("use effect is triggered");
    document.title = `You clicked ${count} times`;
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <p>You clicked {count2} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={() => setCount2(count2 + 1)}>Click me 2</button>
    </div>
  );
}

So we just duplicated counter logic. As you can see in browser every change of count or count2 triggers the component rerender but our effect is only called when count changes.

At this point you might ask why it is triggered at the first rerender? Obviously because our count was changed to it's default value zero.

So when we need to do something when a variable changes we can easily achieve this with useEffect and dependencies array.


The next question which is the most popular question about hooks i think is "How to fetch some data on component initialize". And normally the answer that you will find "Just provide an empty array as a dependency to effect".

Let's try this out. As you can see it is working correctly but it's the completely incorrect mentality to work with hooks and all people who wrote classes in React have this mentality.

We are trying to apply classes knowledge and patters to hooks. Like we had componentDidMount where we fetched data and we want something similar.

The way that we need to thing while working with hooks is defining the dependencies on properties and stop thinking about just triggering something on initialize or on change. Our components should be build in the way that simply reacts somehow on data changes. And we define how. It's one of the main problems that stops people on mastering hooks.

So empty array doesn't mean that it's the same like componentDidMount. It just means that you don't have any dependencies that we retrigger your effect.


One more important point to mention is that if you don't provide the dependencies inside your useEffect it will be called after each rerender and it's the default behaviour.


Now let's make an example with fetching data inside useEffect hooks because it's actually the most popular user of useEffect. We don't have any API to fetch data and I prefer to use json-server to build fake API fast.

Let's install json-server globally.

npm install -g json-server

Now we can define db.json file which will be our database.

{
  "posts": [
    {
      "id": "1",
      "title": "Use state hooks"
    },
    {
      "id": "2",
      "title": "Use effect hooks"
    }
  ]
}

Now we just need to start our json-server

json-server --watch db.json --port 3004

The problem here is that json-server as create-react-app starts the server on 3000 port so we need to specify other port to make it working.
As you can see now we have access to our local API that we created.

As React doesn't have a library to fetch data we need to install it. I recommend axios library because it's the most popular and easy way to make API requests.

npm install axios

And now we can fetch data inside our useEffect

import axios from "axios";

function App() {
  const [apiData, setApiData] = useState(null);

  useEffect(() => {
    console.log("use effect is triggered");

    axios.get("http://localhost:3004/posts").then((res) => {
      console.log("res", res);
      setApiData(res.data);
    });
  }, [count]);

  return (
    <div>
      <p>You clicked {count} times</p>
      <p>You clicked {count2} times</p>
      <button onClick={() => setCount(count + 1)}>Click me</button>
      <button onClick={() => setCount2(count2 + 1)}>Click me 2</button>
    </div>
  );
}

As you can see we just called our setter from useState after getting the data. This is how our data from API are getting inside the component. No we can simply render this data as a normal array.

{apiData && apiData.map((item) => <div key={item.id}>{item.title}</div>)}

And the last thing that I want to mention is that it's super easy to get and infinite loop with useEffect. If we just simply remove dependencies array which means that we trigger this fetch after each rerender it will be infinite loop. Why? Because we trigger useEffect after each rendering and then inside we set state which will trigger rerender again. So you should really know what you are doing.

So this were the most important parts of using useEffect hooks.

If "React hooks for beginners" is too easy for you I have a full hooks course which is going 8 hours where we are creating real application from scratch. I will link it down in the description below.

📚 Source code of what we've done