DEV Community

Cover image for React Hooks (useEffect)
Ryan Kuruppu
Ryan Kuruppu

Posted on

React Hooks (useEffect)

πŸ”– Table of Contents

  1. The Task
  2. Setup
  3. Adding the useEffect hook
  4. Structure of the useEffect hook
  5. The dependency array
  6. Example with dependencies and cleanup

So this section of the tutorial is going to speak about the useEffect hook. What it is, how it works and how it fairs compared to the existing 3 class functions given to use by React.

❗ Just for your Information

I won't go into too much detail and I'm going to keep this brief to a point where it'll make sense but I do suggest that after you finish this tutorial. Read the links I attach below for a deeper understanding especially the explanation by Dan Abramov the creator of React.

There's a bit of reading here but bare with.

What is the useEffect hook?

The useEffect hook is a replacement for 3 of React's class-based lifecycle functions namely,

  1. componentDidMount
  2. componentDidUpdate
  3. componentWillUnmount

As of React 16.8 and React Native 0.59, the useEffect hook combines all 3 of these functions into one single function with a cleaner and concise look.

Just like the 3 earlier functions, you would do side effects like Data Fetching, Subscriptions etc. from within the useEffect hook(s)

Note how I said "hook(s)" with an s.

That's right! Your functional component can have multiple useEffect hooks to handle different actions within your code.πŸŽ‰πŸŽ‰

So how is this gonna work?

First I'm gonna describe the task we'll be using to demonstrate the useEffect hook then we'll go through the useEffect hook and its structure and we'll initially write the component in a class component so you understand how it works (also so the beginners understand if any). Then finally we'll talk about the pros and cons of using it compared to your usual class functions.

So lets get started.


The Task

We'll keep it simple. Sweet. Like Music 🎡

We're simply going to use the site JSON placeholder to generate some random Post (comments, username etc.) data for us and we're going to pull that into our component using class lifecycle functions first and then hooks and display it in our page.

After this. We're going to demonstrate another use case where we'll be attaching an event listener to our component to monitor the scroll position. This will help us understand the cleanup dependencies a little better.

Let's start writing.

  1. We'll start by laying down the foundation for our component. So we'll use our App.js file which comes by default in create-react-app

Just make sure your App.js file looks like this.

React from 'react'

class App extends React.Component{
    render(){
        return <div></div>
    }
}

export default App;
Enter fullscreen mode Exit fullscreen mode
  1. Let's use a state variable like in the previous tutorial to hold all our posts.

      React from 'react'
    
      class App extends React.Component{
    +    constructor(){
    +        this.state = {
    +            posts = []
    +        }
    +    }
    
         render(){
            return <div></div>
         }
      }
    
      export default App;
    
  2. Now let's fetch the data from JSON Placeholder using ComponentWillMount

    React from 'react'
    
    class App extends React.Component{
        constructor(){
            this.state = {
                posts = []
            }
        }
    +
    +   componentDidMount(){
    +       fetch("https://jsonplaceholder.typicode.com/posts")
    +           .then((response) => response.json())
    +           .then((data)=>this.setState({posts: data})
    +   )}
    +
        render(){
    -     return <div></div>
    +     return( 
    +         <div>
    +          {posts.map((p)=>{
    +             return (
    +                  <div key={p.id}>
    +                    <h1>{p.title}</h1>
    +                    <h4>{p.subtitle}</h4>
    +                  </div>
    +              )
    +          })}
    +          </div>
    +     )
       }
    }
    
    export default App;
    

Normal so far cause well.. this is how you'd write it in the "Pre-Hook" Era.

With this you should be able to successfully get data from the frontend.

Now lets start changing things up

So lets start by changing up the component and it's state to a hook based state like the previous tutorial

import React, {useState} from 'react'

function App(){
  const [posts, setPosts] = useState([]);

  return(
    <div></div>
  )
}

Enter fullscreen mode Exit fullscreen mode

Next lets introduce the useEffect hook.

The useEffect hook, like I stated before. Has 3 important parts. This short example will use just 1 part. The Action which in our case is the fetch request for all the posts.

So let's add it and display them.

+ // Import useEffect
 import React, {useState, useEffect} from 'react'

  function App(){
    const [posts, setPosts] = useState([]);

   //Add the useEffect.
+   useEffect(()=>{
+      fetch("https://jsonplaceholder.typicode.com/posts")
+        .then((response) => response.json())
+        .then((data)=> setPosts(data));
+   },[]);

    return (
-     <div></div>
-   );
+   return(
+     <div>
+       {/* Render Posts */}
+       {posts.map((p)=>(
+           <div>
+             <h1>{p.title}</h1>
+             <h4>{p.subtitle}</h4>
+           </div>
+       )}
+     </div>
+   )
+ }

Enter fullscreen mode Exit fullscreen mode

✨And thats it !!✨

So let see what we exactly did with the component.

The useState hook is similar to what we did in the previous tutorial.

The useEffect is the new part here for us.

Let's see how it works.

The Structure

Alt Text

The callback function (A function which is passed as an argument to another function) is the first argument of the useEffect hook.

It is inside this function that we define our side effect, data fetching calls, subscriptions or listeners. It runs after the component renders so that the component render isn't blocked by the code running within the hook.

Nothing much to explain there

The thing we SHOULD notice is the second argument

The Dependency Array 😡

Alt Text

What the heck is a dependency array? You may ask.

The dependency array as the name says, is essentially a list of variables that your component will "watch" for changes in.

When a variable in the dependency array changes, your component will save a snapshot (temporary copy) of your component state and make a new call to the callback function you created inside your useEffect hook essentially leading to a rebuild of you React Component.

The dependency array is an optional argument and you can omit it but you can also include as many dependency variables as you like. But be smart about it.

However, even though the dependency array is optional. Leaving it out will cause an infinite loop of function calls.

Try it out and see what happens by copying this useEffect into your function and running it.

useEffect(()=>{
    console.log("Hello");
});
Enter fullscreen mode Exit fullscreen mode

You'll notice that in your console. You'll end up causing an infinite loop. This is really confusing which is why I urge you to read this article by Dan Abramov which I've listed at the end of the tutorial as well.

Cool. That's fine. So then why does our example above have an empty array? (snippet is below this)

useEffect(()=>{
    fetch("https://jsonplaceholder.typicode.com/posts")
      .then((response) => response.json())
      .then((data)=> setPosts(data))
},[]);
Enter fullscreen mode Exit fullscreen mode

Well, simply put. Providing an empty dependency array tells React to only run this specific useEffect hook ONLY on the FIRST Render of our component. This is when the component is mounted into the DOM essentially replacing our componentDidMount function.

Okay so how do I update the UI using useEffect?

Since you hopefully understood how the useEffect works theoretically. I'll show you the second example I mentioned here

We'll create a component that monitors the scroll position of the UI using useEffect and useState.

We'll also go through the "clean up" part of the useEffect hook and what it does.

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

function ScrollWatcher(){

    const [offsetValue, setOffsetValue] = useState(0);

    useEffect(()=>{
        window.onScroll = function(){
          setOffsetValue(window.pageYOffset);
       }

       return ()=>{
           window.onScroll = null;
       }
    }, [window.pageYOffset]);

    return (
       <div style={{height: "1000vh"}}> 
           {offsetValue} 
        </div>
    )

}

Enter fullscreen mode Exit fullscreen mode

So just to describe what we've done here.

When the component mounts, we tell our useEffect hook to add a listener to the window scroll event and update the state.

The points of interest here are again,

  1. The Dependency Array
  2. The return function

The dependency array now has the window.pageYOffset variable added to it. What this means is that whenever the window.pageYOffset value changes. The useEffect hook will re-run and update the offsetValue state variable.

The return function here is the action we will carry out when the component is about to unmount. This is the reason we call it the "clean up" function. This is where we will remove any subscriptions or listeners attached to the component or its variables.

And thats it. It wasn't much of an explanation but hopefully you got the gist of it. Just some final tips before I suggest some articles you should definitely have a look at.

Tips

  1. You can create multiple useEffect hooks. One for data fetching one for subscriptions for example.
  2. If you create multiple useEffect hooks, note that they are executed in the order they are written.
  3. Read as much as you can about the useEffect hook to better understand how this works. It can save you the world of time when debugging your code.

Other than that,

Hope you enjoyed !😊

Let me know what you think in the comments below !

Read my tutorial on the useCallback hook (Will be added in the future)


Further Reading

Discussion (0)