React useEffect Hook: Use cases
Tags: webdev, javascript, programming, react
Published: 2023-07-29 09:21:05

React **useEffect** is a react hook function that was introduced in React version 17; used for executing side effects following the mounting of a React component or as a result of changes in state declared variables. The hook expects 2 arguments; A callback function and an optional array of dependencies,it  optionally  return a function.

Visually, the useEffect function was developed by the React team with its functionality abstracted away and only the  business logic (arguments, and return function) accessible to end users. 
_Here is a React function component we will be using for our use cases:_

```javascript
function BlogPosts() {
  const [posts, setPosts] = React.useState([]);
  const [error, setError] = React.useState("");
  const [pending, setPending] = React.useState(false);
  const textInput = React.useRef(null);
  const form = React.useRef(null);

  const getPosts = async () => {
    let url = `https://jsonplaceholder.typicode.com/posts/`;
    try {
      setPending(true);
      const response = await fetch(url);

      if (response.ok) {
        const payload = await response.json();
        setError(""); //clear previous error message if any
        return payload;
      } else throw new Error("Something went wrong....");
    } catch (error) {
      setError(error.message);
      return error;
    } finally {
      setPending(false);
    }
  };

 /*we will add a useEffect hook here*/

  // render some jsx
  if (pending) return <p>Loading Posts...</p>; // pending state

  if (error.length > 0) return <p>{error}</p>; // rejected
  // you can create a <PostsComponent/>  of your own
  if (posts.length > 0) return <h3>Received {posts.length} posts.</h3>; // resolved

  return null;  /* just before we fetch posts show blank screen*/

  
}
```

### Use cases:

1. Performing side effects
2. Tracking dependencies 
3. Setup and tear down processes
4. Manipulating the DOM
5. UseEffect can NOT be converted to async function.

### 1. Performing side effects
The hook can be used  to perform executions after a component is mounted. This includes: network requests by  fetch data from a rest API, initializing  animations,setting up app related event listeners, setting timeouts and intervals.

_Add this piece of code below the getPosts function_
```javascript
React.useEffect(() => {
    // fetch data
     getPosts().then((res) => {
      setPosts(res);
    })


    // start timers
    setTimeout(() => {
      console.log("I am executed after 5 seconds");
    }, 5000);
    setInterval(() => {
      console.log("I am executed after every 5 seconds");
    }, 5000);
    // app event listeners
    addEventListener("resize", () => {
      console.log("Window resized");
    });
    addEventListener("offline", () => {
      console.log("App is offline");
    });
    addEventListener("online", () => {
      console.log("App is back online");
    });
    
  });
```




### 2. Tracking Dependencies
Our Our useEffect function up there runs every time there is a render introducing something that looks like a bug.

UseEffect hook runs everytime the UI is rendered  and when the component unmounted. The UI is always re-rendered  whenever a state variable (variable declared by useState hook) changes.
 Sometimes, though, you only want to run useEffect once or when a particular state variable changes. 

The second optional argument supplied to useEffect is an array of dependencies, it determines how the useEffect hook  is called; an empty array triggers the useEffect  during initial render and when the component is unmounted while an array with elements will trigger the hook whenever there are changes are in one of the array elements.

_we add an empty array as our second argument so that our hooks runs once_
```javascript
React.useEffect(() => {
    // fetch data
     getPosts().then((res) => {
      setPosts(res);
    })


    // start timers
    setTimeout(() => {
      console.log("I am executed after 5 seconds");
    }, 5000);
    setInterval(() => {
      console.log("I am executed after every 5 seconds");
    }, 5000);
    // app event listeners
    addEventListener("resize", () => {
      console.log("Window resized");
    });
    addEventListener("offline", () => {
      console.log("App is offline");
    });
    addEventListener("online", () => {
      console.log("App is back online");
    });
    
  },[]);


```

Primitive data types such as numbers,strings and booleans, UseEffect can easily track their changes while non-primitives such as as objects and arrays are mutable data types stored by reference, useEffect has a problem tracking their changes since “non-primitives refer to the same instance” that is they always look identical.

To make useEffect track changes in non primitivies:
1. **UseMemo** hook can be used in conjuction with useEffect to track changes in an array. It calcualates changes between elements previous array and the next array through memoization.

```javascript
// useMemo will trigger useEffect if posts array has changed
Const _postChanges=React.useMemo(()=>posts,[posts]) /*return the posts array inside useMemo hook*/
/* now the useEffect will be able to listen to changes on posts array by tracking _postChanges array from useMemo */
React.useEffect(()=>{
console.log('you have changes in your array')
},[_postChanges])

```
2. Tracking an object property instead, we assume every object always has a unique property mostly `id` property.


```javascript
const [post,setPost]=React.useState({id:1,title:"UseEffect hook"})
const nextPost={id:2,"UseReducerHook"}

React.useEffect(()=>{
console.log(`The new post id is ${post.id}`)
},[post?.id]) // runs whenever post id changes

return(<div>
<p>The current post is {post?.title} </p>
<button onClick={()=>setPost(nextPost)}>Next Post</button> </div>)
```

### 3. Setup and tear down processes

UseEffect hook comes in handy when setting up processes that can be used when the components mounts and removed when it unmounts, e.g setTimeout, setInterval, adding and removing event listeners.
The hook returns a function that can be used to perform these tear down processes.

_ we now declare two new variables for clearing setInterval and setTimeout, we then return a teardown function_
```javascript
React.useEffect(() => {
    // fetch data
     getPosts().then((res) => {
      setPosts(res);
    })
let timeout,interval; // for clearing intervals and timeout

    // start timers
    timeout=setTimeout(() => {
      console.log("I am executed after 5 seconds");
    }, 5000);
   interval= setInterval(() => {
      console.log("I am executed after every 5 seconds");
    }, 5000);
    // app event listeners
    addEventListener("resize", () => {
      console.log("Window resized");
    });
    addEventListener("offline", () => {
      console.log("App is offline");
    });
    addEventListener("online", () => {
      console.log("App is back online");
    });
return function(){
// tear down events and intervals
 if(timeout)clearTimeout(timeout);
if(interval)clearInterval(interval)

removeEventListener("resize", () => {
      console.log("Window event removed");
    });

}
   

  },[]);
```


### 4. Manipulating the DOM
  React implements a virtual DOM before reconcilling changes to the real DOM  on the browser. Most a times you want to access to a DOM element such as form or form input elements. We can gain access to the DOM using **useRef** hook then manipulate it inside the useEffect  hook. Sometimes you can use DOM API methods  such as `document.getElementById()` since we know the DOM is always mounted. It is not advisable though; go for useRef hook.


```javascript
React.useEffect(() => {
    // fetch data
     getPosts().then((res) => {
      setPosts(res);
    })
let timeout,interval; // for clearing intervals and timeout

    // start timers
    timeout=setTimeout(() => {
      console.log("I am executed after 5 seconds");
    }, 5000);
   interval= setInterval(() => {
      console.log("I am executed after every 5 seconds");
    }, 5000);
    // app event listeners
    addEventListener("resize", () => {
      console.log("Window resized");
    });
    addEventListener("offline", () => {
      console.log("App is offline");
    });
    addEventListener("online", () => {
      console.log("App is back online");
    });
/*MANIPULATE THE DOM */
 // access the input on the DOM and make put placeholder after 2 seconds
    setTimeout(
      () => {
        if (textInput.current) {
          textInput.current.setAttribute("placeholder", "Enter post title....");
          textInput.current.focus();
        }
      },

      3000
    );
    // manipulate the DOM, give some time out to make sure its there
    setTimeout(() => {
      const formWrapper = document.getElementById("form-wrapper");
      if (formWrapper) formWrapper.style.backgroundColor = "red";
    }, 100);
    // use DOM API to change background to light gray
  }, []);
/*CLEAN UP THE COMPONENT*/
return function(){
// tear down events and intervals
  if(timeout)clearTimeout(timeout);
if(interval)clearInterval(interval)
removeEventListener("resize", () => {
      console.log("Window event removed");
    });

}
   // Also update the jsx returned
  let style = { display: "block", width: 200, padding: 5 };
  let btnStyle = {
    ...style,
    background: "hotpink",
    padding: 5,
    borderRadius: 5,
    marginTop: 10,
    color: "white",
  };
// render components
  if (pending) return <p>Loading Posts...</p>; // pending state

  if (error.length > 0) return <p>{error}</p>; // rejected 

  return (
    <div style={{ padding: "1rem" }} id="form-wrapper">
      <form onSubmit={handleSubmit} ref={form}>
        {posts.length > 0 && <h3>Received {posts.length} posts.</h3>}
        <div>
          <label htmlFor="username" style={style}>
            Enter Post Title
          </label>
          <input id="username" style={style} ref={textInput} />
        </div>

        <button style={btnStyle}>Submit</button>
      </form>
    </div>
  );

  },[]);
```


#### 5. UseEffect can NOT be converted to async function.

React hook has few special rules; 
1.) React hooks only work inside React components.
2.) React hooks can only be declared on top level scope inside a react component. They cannot be declared conditionally.

```javascript
Const [deadline,setDeadline]=React.useState(true);
// DON’T
if(deadline){

useEffect(()=>{},[])
}
// DO
useEffect(()=>{
if(deadline){
// do something about it
}
},[])

```


Most React beginners are always eager  break these rules  with ease.
There is another unwritten rule that React frowns upon; making the callback provided to useEffect as async/await function.

```javascript
// DON'T
React.useEffect(async()=>{
const payload=await getPosts();
setPosts(payload);
},[])

```


When  callback used as an async/await function it becomes a promise that blocks the main thread until it settles  instead of simply running effects and returning a function.  
We run async functions inside useEffect this way:

```javascript

React.useEffect(()=>{
 getPosts().then(res=>setPosts(payload)).catch(err=>console.error(err.message));
},[])

```

Alternatively we can use an immediately invoked function expressions (IIFE) inside useEffect like so:

```javascript
React.useEffect(()=>{
(async()=>{
const payload=await getPosts();
if(payload) setPosts(payload)
})()
 
},[])
```