DEV Community

Ahmet Meliksah Akdeniz
Ahmet Meliksah Akdeniz

Posted on

React | Fetch Data with useEffect Hook

Hello folks, today I will be talking about useEffect hook in React.

Previosly I've written about about useState hook if you want to read that; part 1 and part 2

So, what's useEffect? In short, we use this hook to tell React that our component needs to do something after each render. React will keep that (useEffect function) in mind, and trigger it after performing DOM updates. This can be used in many scenarios, but today I will use it for fetching data.

Fist, I will explain how it works, then I will use it for fetching data.

This is index.js component:

import React from "react";
import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
  <React.StrictMode>
    <App />
  </React.StrictMode>
);
Enter fullscreen mode Exit fullscreen mode

App.js component:

import { useEffect } from "react";

// css is from boiler plate (code that comes with React), I didn't code anything for styling
import "./App.css";

function App() {
  useEffect(() => {
    console.log(`useEffect comes AFTER page render`);
  });

  console.log(`Page is rendered`);

  return (
    <div className="App">
      <h1> useEffect </h1>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

If you go to console, you should see Page is rendered on top of useEffect comes AFTER page render because useEffect runs after each render. Now, this is out of the way, let's talk about dependency list in useEffect a bit. I will add a counter that only increments the value in App.js file.

Here's App.js again:

import { useState, useEffect } from "react";

// css is from boiler plate (code that comes with React); however, I added a bit too :D
import "./App.css";

function App() {
  const [counter, setCounter] = useState(0);

  useEffect(() => {
    console.log(`useEffect come AFTER page render`);
  });

  const increment = () => {
    setCounter((prevVal) => prevVal + 1);
  };

  console.log(`page is RENDERED`);

  return (
    <div className="App">
      <h1> useEffect </h1>
      <h2> {counter} </h2>
      <button onClick={increment} className="btn">
        Increment
      </button>
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

In short, the code above has a button that increments value of counter. I don't want to be repetitive, so if you've got no clue about useState you can check this. It helps understand how increment function works

However, if you go to console on the browser, you will notice each time the value changes, useEffect hook gets triggered.

showing hook

The reason for that is useEffect is called after every render except if you add a second argument; depency list. If you want to fire useEffect only in initial render, then you've got to add an empty array as a second argument like so:

useEffect(() => {
    console.log(`ONLY INITIAL RENDER`);
  }, []);
Enter fullscreen mode Exit fullscreen mode

only in initial render

As you can see from the image above, it's rendered only once. OK... But what's the use of this thing that renders only once? This will prevent us from falling into an infinite loop when calling a function in useEffect, and for this example that function will be fetching data.

For now I will use a classic; https://jsonplaceholder.typicode.com.

Time to share the whole code and explain it in small chunks:

import { useState, useEffect } from "react";

// css is from boiler plate (code that comes with React); however, I added a bit too :D
import "./App.css";

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

  const fetchUsers = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();

    setUsers((prevUsers) => (prevUsers = users));
    console.log(users);
  };

  useEffect(() => {
    fetchUsers();
  });

  return <div className="App"></div>;
}

export default App;
Enter fullscreen mode Exit fullscreen mode

First, we need to import useEffect and useState like so; import { useState, useEffect } from "react";.

const fetchUsers = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();

    setUsers((prevUsers) => (prevUsers = users));
    console.log(users);
  };
Enter fullscreen mode Exit fullscreen mode

The function above should be asynchronous because we can never anticipate when the data will come, and without data we shouldn't be executing lines below (at least not without error handling), so I used async, await syntax. As you can see, I fetched data from jsonplaceholder.typicode.com (obviously, used the exact "path" to get data) by using this line of code const response = await fetch("https://jsonplaceholder.typicode.com/users");. However, to work with that data, I got to convert it to a json, so I converted it to json like so; await response.json(). Finally, put that json into a variable called users.

Note:
Do not confuse users variable that I use with users variable that I used in hook (const users VS. const [users, setUsers]). Remeber, there's scope in JavaScript. So, users variable that's inside of fetchUsers function is not accessible outside of that function.

Finally, the code above sets users (in fetchUsers function) variable to const [users, setUsers] hook by this
code setUsers((prevUsers) => (prevUsers = users));.

Note:
useState hook automatically receives previous value as argument, and I like being explicit when I updat value of state. So, I explicitly write prevVal = data when I set new data to it. If it was a counter, I would've written setCounter((prevCount)=> prevCount + 1). However,setUsers(users) would also work.

We could use axios to make fetching easier, but this is a really simple example, so I didn't bother using it. Keeping axios for later

The problem with the code above is that it will drag us down to an infinite loop. Why? Because useEffect runs after each render. Let me show the code again

const fetchUsers = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();

    setUsers((prevUsers) => (prevUsers = users));
    console.log(users);
  };

  useEffect(() => {
    fetchUsers();
  });
Enter fullscreen mode Exit fullscreen mode

Code above will run fetchUsers function after initial render. Then, fetchUsers function will get data, and page will re-render. After that useEffect will run again. So, data will be fetched again, then useEffect will run again and so on. To avoid this, we add second argument which is an empty array, so this useEffect runs only on initial render.

useEffect(() => {
    fetchUsers();
  }, []);
Enter fullscreen mode Exit fullscreen mode

OK, so we fetched data, now let's use it. It will be an extremely simple usage though, I added this to the bottom of App component only:

return (
    <div className="App">
      {users.map((user) => {
        return (
          <div key={user.id}>
            <h2> {user.name} </h2>
          </div>
        );
      })}
    </div>
  );
Enter fullscreen mode Exit fullscreen mode

The code above, maps over users array and displays names of users in h2 tag. Logic behind the code above is similar to logic here. If that's not clear, please read this. Other than that, since list rendering is not topic of this blog post, I won't be covering that.

Final code looks like this:

import { useState, useEffect } from "react";

// css is from boiler plate (code that comes with React); however, I added a bit too :D
import "./App.css";

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

  const fetchUsers = async () => {
    const response = await fetch("https://jsonplaceholder.typicode.com/users");
    const users = await response.json();

    setUsers((prevUsers) => (prevUsers = users));
    console.log(users);
  };

  useEffect(() => {
    fetchUsers();
  }, []);

  return (
    <div className="App">
      {users.map((user) => {
        return (
          <div key={user.id}>
            <h2> {user.name} </h2>
          </div>
        );
      })}
    </div>
  );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

Alright that was it for today, we briefly talked about useEffect hook. The source that I used if React documentation by the way.

Top comments (5)

Collapse
 
ahmetmeliksah profile image
Ahmet Meliksah Akdeniz

I will definitely look into this, thank you for your feedback!

Collapse
 
naucode profile image
Al - Naucode

Great article, keep the good work! Liked and followed! 🚀

Collapse
 
ahmetmeliksah profile image
Ahmet Meliksah Akdeniz

Thank you so much, very much appreciated!

Collapse
 
focus_your_goal profile image
SpecialForces 🇹🇷🇦🇿

Great article thanks to sharing! >.<

Collapse
 
ahmetmeliksah profile image
Ahmet Meliksah Akdeniz

Thank you!