Advanced React Hooks: Everything About the UseEffect | by Cihan | Aug, 2022

You need to know to level up your projects

Photo by Clay Banks on Unsplash

What we’ll reading today:

1. What is the useEffect?
2. Understanding the useEffect Dependency Array
3. Understanding the useEffect Cleanup Function
4. What is the Infinite Loop in the useEffect?
5. Usage of the async-await Functions in the useEffect
6. Tips and Tricks for Using the useEffect Effectively

Let’s talk about the useEffects in React Hooks!

The useEffect is a hook that can be used to replace some of the React Lifecycle methods. So the useEffect behaves similarly to the class lifecycle methods. The useEffect is used to perform on a functional component in the following cases.

  1. when the component is rendered (componentDidMount in class-based function)
  2. when the component is updated (componentDidUpdated in class-based function)
  3. when the component is removed from the DOM (componentWillUnmount in class-based function)

Some examples of side effects:

  • Fetting data
  • Directly updating the DOM
  • Setting the page title
  • Working with setInterval or setTimeout
  • Measuring the width, height, or position of elements in the DOM
  • Setting or getting values ​​in local storage
  • Subscribing and unsubscribing to services

The useEffect takes two arguments. The first argument is a callback function for which we will perform the side effects; the other is a dependency array. The second argument is optional.

  1. If we don’t pass the second argument, the side effect in the callback function will run again every time the component is rerendered.
function MyComponent() {
useEffect(() => {
// The side effect will run after every render
})
}

2. If we pass the second argument as an empty array, the side effect in the callback function will only run once when the component is rendered for the first time.

function MyComponent() {
useEffect(() => {
// This side effect will only run once, after the first render
}, [])
}

3. If we pass the props or state values ​​in the second argument, the side effect in the callback function will only run when the props or state values ​​change.

import { useEffect, useState } from 'react'
function MyComponent({ prop }) {
const [state, setState] = useState('')
useEffect(() => {
// the side effect will only run when the props or state changed
}, [prop, state])
}

NOTE: The useEffect uses shallow comparison to compare the useEffect’s dependency values.

  • The callback function will run after each render if there is no dependency array.
  • If there is an empty dependency array the callback function will only run once after the first rendering
  • If there is a dependency array with the props or state values, the callback function will only run when these values ​​change

The second argument of the useEffect allowed us to run side effects when the component is mounted and updated. We needed to run the side effects when the component is also unmounted. It is a cleanup function that allows us to stop the side effects just before the component is unmounted.

function MyComponent() {
useEffect(() => {
// this side effect will run after every render
return () => {
// this side effect will run before the component is unmounted
}
})
}

Here’s an example of use in a real-world application:

An example of use in a Real-World Application

Although using useEffect is common, it takes some time to master it. That’s why many new users can set the useEffect to cause an infinite loop. In this section, I’ll describe the common scenarios that generate infinite loops and how to avoid them.

  1. If the dependency array is not specified
function App() {
const [users, setUsers] = useState([])
useEffect(() => {
const getUsers = async () => {
const {data} = await axios.get("/api/user")
setUsers(data)
}
getUsers()
}) // without dependency array
}

Problem

The users’ value (state) changes when the component is rendered. Because the state has changed, the component is rendered. Because we didn’t specify the dependency array, the useEffect is running again, and the state is changing again.

Solution

I hope there is no need to explain the solution. If you want explanation, please let me know in the comments.

2. If a function is specified in the dependency array

A Function as Dependency

Problem

We know that the useEffect makes shallow comparisons. The useEffect Performs a shallow comparison to verify whether dependencies have been updated. With setCount, state is updated when the component is rendering for the first time.

Because the state is updated, the component is rendered again. Because getResult is a function, the reference value in memory is recreated every time the component is rendered. Therefore the result of shallow comparison returns false. Thus, the infinite loop is formed.

Solution

Memoized the getResult Function

With useCallbackthe getResult function is memoized. This ensures that the reference value of the getResult function does not change. When the useEffect makes a shallow comparison, it returns true, and the component is not rendered.

Note: There are many ways to avoid infinite loops in Component. I’ve only mentioned two way.

We need to perform async operations if we want to get data using the API. How can we do these operations with the useEffect?

1. Create the async function outside of the useEffect and invoke it in the useEffect.

const getPosts = async () => {
const {data} = await axios.get('api/posts')
setPosts(data)
}
useEffect(() => {
getUsers()
}, [])

2. Create the async function in the useEffect and invoke it in the useEffect.

useEffect(() => {
const getPosts = async () => {
const {data} = await axios.get('api/posts')
setPosts(data)
}
getUsers()
}, [])

3.Use IIFE (Immediately Invoked Function Expression) in the useEffect

useEffect(() => {
(async () => {
const {data} = await axios.get('api/posts')
setPosts(data)
})()
}, [])

If you have any questions, please let me know in the comments.

// DON'T DO THIS!!!
useEffect( async () => {
const {data} = await axios.get('api/posts')
setPosts(data)
}, [])

Let’s look at some tricks that we can use in the useEffect.

1. The useEffect must be used at the top level

if(a > b){ 
useEffect(() => {
// incorrect usage
}, [])
}
useEffect(() => {
if(a > b){
// incorrect usage
}
}, [])
useEffect(() => {
if(a < b) return
// correct usage
}, [])

You cannot use the useEffect in conditional expressions, loops, and nested functions.

2. Use the useEffect for a single work

Do only one work in the useEffect. For more than one work, you can use more than one the useEffect on the same component. Divide each of your works into these the useEffects. Dividing useEffect into short and single-purpose functions also prevents unwanted rerenders and ensures that the code remains clean and readable.

Incorrect use of the useEffect

useEffect is a very powerful and widely used React hook that can be mastered over time. As you get used to it, it will be the tool you use the most.

Want to Connect?LinkedIn — Twitter — GitHub

Leave a Comment