A Simple Explanation of useEffect hook with code examples - part 19

A complete guide to the useEffect React Hook

React components are pure functions, which means they receive prop and state and reflect the result on the screen, but in the real world, we need to perform the action related to the outside based on the state inside React.

The useEffect is one of the most used hooks by React developers because it lets you build more productive web applications. It lets you perform the tasks that need to synchronize components with external systems.

To put it simply, React and non-React based systems can exchange data and connect with one another. To provide real-time information on a web page or to submit users to an external system, useEffect will make sure that both the React component and the external system or API are synchronized. 

If you're new in React, useEffect hook can be a little challenging, let me allow you to uncover it step by step so it'll quickly make sense to you.

You'll learn the topics as follows:

  1. What are the Effects?
  2. Introduction of useEffect hook
  3. The useEffect hook with and without dependencies
  4. Example of the scroll to top practical example
  5. The useEffect return function
  6. Best practices of using the useEffect 

What are the Effects?

In React pure function, components can predict the outcome but if it is difficult to predict the behavior like how constant might be used or modified by others is called Effects.

Side effects refer to any interaction outside of React.

  • Updating DOM such as fetching data from an API and displaying it on the page and updating a document title in response to user input.
  • Sending HTTP requests and receiving responses.
  • You can connect to the network, use third-party libraries or other third-party widgets.

By specifying the side effects in useEffect hook, you can ensure that side effects execute only after the component renders or update. Remember that performing such a task during rendering is not ideal.

Basic of useEffect to manage state and side effects in React

The useEffect is a hook in React that allows React developers to control the behavior of how React components will communicate with the outside world. It could be used for managing the state and side effects at the same time.

The basic syntax of useEffect 

You may have noticed the following syntax if you have been following a useEffect-based lesson for a while:

The useEffect with call back function 

 useEffect(() => {
   // executes side effects 
 })

The useEffect with call back function and array of dependency 

 useEffect(() => {
   // executes side effects based on dependency array 
 }, [name])

The useEffect hook with call back function and an empty array of dependency 

 useEffect(() => {
   // side effects with no dependency 
 }, [])

The whole article will explain these syntaxes with real-time examples.

Change page title in React 

If you're working directly with DOM, is side effects in React. In our example, we'll use useEffect to render the username in document title.

import useEffect hook beside useState and useRef from React and declare useEffect hook in functional component.

import { useEffect, useState, useRef } from "react"
const DocumentTitle = () => {
  const [username, setUsername] = useState('elpeeda')
  const userRef = useRef(null)

  useEffect(() => {
    document.title = `Hello, ${username}`
  })
  const handleSubmit = (e) => {
    e.preventDefault()
    document.title = `Hello, ${username}`   
  }
  const handleUsernameChanges = (e) => {
    setUsername(e.target.value)
  }
  return (
    <div>
      <form onSubmit={handleSubmit}>
        <input type="text" ref={userRef} value={username} onChange={handleUsernameChanges} />
        <button type="submit">submit</button>
      </form>
    </div>
  )
}
export default DocumentTitle

In this example, useEffect hook update the title of the page with current username in response to user input.

The code is working all right, but this code effect will execute every time the component re-render, leading to unnecessary rendering. Here, let me introduce the dependency array, which is the second argument of syntax in useEffect hook.

useEffect hook with array of dependency

The default behavior of the useEffect hook is to run side effects after each render; even a single key press in an input field will trigger a new render of the component and the execution of side effects.  You can ignore this behavior in small applications, but by now you already know, React is often used for more complex UI dashboards.

The useEffect hook is determined by the dependency array, which controls when the side effects should be re-run. If the state changes between re-rendering, useEffect will execute the side effects. Let's include the username in the dependency array.

 useEffect(() => {
    document.title = `Hello, ${username}`
  }, [username]) 


When we specify the username state variable as a dependency in useEffect, React will compare the current value of username in the array to the previous value of username on every render and only execute the side effects if the value has changed.

If the value is same between re-render the effects won't re-run which will save the unnecessary updates to the document title.

In simple words by providing the second argument to useEffect, you'll make sure side effects only run once when the component is mounted and after only the value of dependency has changed.

useEffect hook with an empty array of dependency

You also have an option to not provide any value in the array and keep it empty. The empty array of dependency means effects have no dependency.

In this case, the effects will only run once when the component is mounted. It's useful when you want you to want to execute code only once such as to create fade-in animation or connect to the server.

Let's get back to the previous example; updating the page title. If I opt out of state variable and only provide an empty array-

 useEffect(() => {
    document.title = `Hello, ${username}`
  }, []) 

React will only execute useEffect statement once, after the initial render. Now updating the username won't have any effect.

Use cases of useEffect

  • Fetching data from an API and updating components with retrieved data on the screen.
  • Subscribe to events like resizing the browser window.
  • Modifying Dom in response to state changes.
  • Managing state that update or change in response to other activities like validation.

Practical use case: scrollTo the top in the useEffect

The button will appear once the user has scrolled past a particular point, and when it is clicked, the page can be scrolled to the top.

I already demonstrate this example in the useRef tutorial to practically understand Refs, but here, I'll use useEffect to attach an event listener to determine the scroll position.

 import { useState, useEffect } from 'react'
 function App() {
  const [showBtn, setShowBtn] = useState(false)
  useEffect(() => {
    const handleScroll = () => {
      if (window.pageYOffset > 100) {
        setShowBtn(true)
      } else {
        setShowBtn(false)
      }
    }
    window.addEventListener('scroll', handleScroll)

  }, [])

  const handleScrollToTop = () => {
    window.scrollTo({
      top: 0,
      behavior: 'smooth'
    })
  }
  return (
    <div className="App">
      // ...
      {showBtn && <button className="top-button" type="button" onClick={handleScrollToTop}></button>}
    </div>
  )
 }

window.scrollTo function used to scroll smoothly to the top.

The useEffect clean up function

Sometimes you have to stop the Effects before it re-run. For example, suppose you're validating a form and managing state in useEffect, but you don't want to check the validation logic with every keystroke because you're sending a lot of requests. We can use timers here and only send the request once the user has finished.

When we perform side effects such as setting timers or listening to events, it may continue to run even after the component has been unmounted or the dependency has changed. 

You can stop an effect before it runs again by using the clean up method that useEffect returns. 

 useEffect(() => {
   // execute side effects
   
   return () => {
     // run clean up function
   }
 }, [])

React calls the clean up function before each re-run of the effects and the last time the component is unmounted.

Returning to the previous example where we are using scrolls event listener, we should remove the event listener when the component is unmounted, as it's no longer needed.

 useEffect(() => {
    const handleScroll = () => {
      if (window.pageYOffset > 30) {
        setShowBtn(true)
      } else {
        setShowBtn(false)
      }
    }
    window.addEventListener('scroll', handleScroll)
    
    return () => {
      window.removeEventListener('scroll', handleScroll)
    }

  }, [])

Any data or resources that are modified or created during the component lifecycle, such as subscribing to events, fetching data, or connecting to the server, must be cleaned up or stopped, but if data persists even after the component is unmounted, no clean up function is required.

React useEffect dependency array best practices

We learned what a dependency array is and how to add but it is also important to know what we should use as dependencies because if we miss adding necessary dependencies it may lead to unexpected behavior on the other hand if we added dependencies that we should've not, it'll cause unnecessary renderings or we can be stuck in the loop.

The following points we should consider when adding an array of dependencies in useEffect hook.

  • You should include all reactive values whether it's variables, props, or functions that Effects react on like read and update.
  • Any external source, like API or third-party widget that Effects depend on.

Here are some exceptional cases that you might consider not including as a dependency array.

  • A static variable that doesn't need to update on re-render.
  • That variable or function is declared outside.
  • Exclude dependencies if you want to run Effects only once when component mounts.
  • You don't need to pass setUpdating function as a dependency.
  • Don't add built-in API like fetch() and function like focus ().
  • It's recommended to avoid dependencies that may be object and function because it can lead to performance issues.
  • If state depends on the previous value, you can pass the updater function instead and exclude the state variable from the dependency array.
  • You must not include event listeners and timeout/interval functions as dependencies, it may lead your component to an infinite loop.

To avoid these specific issues I mentioned above in both cases missing and adding unnecessary dependencies, you can use a linter like ESLint to catch these common mistakes.

Conclusion

The useEffect hook allows you to synchronize your React application with external systems and perform actions based on changes to state, props, and component lifecycle. By providing the dependency you can control when to run effects. You can build a more complex react application by using this useEffect hook.

Make sure you follow best practices when applying dependencies and take a small step at a time. Let me know your experiences and if you have any queries regarding this topic feel free to ask me in the comment box below.

You can find more information about useEffect hook on an official document.

Synchronizing with Effects

Removing Effects Dependencies

Lifecycle of Reactive Effects



0 Comments:

Post a Comment