DEV Community

guzdaniel
guzdaniel

Posted on

useEffect() & useState() in React

Purpose of using hooks

When React 16.8 was released, support for Hooks was first introduced as a way for developers to use certain existing features of React in an even more declarative manner. These features range from managing a component's state to performing an after-effect or side effect when the state or states of a component change.

Hooks help us write better, clearer and more concise components. They are simpler to work with and test and also help us write more reusable stateful logic. This is all possible without the need to write classes and or implicitly tell React every step of the process.

For managing a component's state, we can use the Hook useState() in our components and for performing side effects after rendering our components to the DOM, we can use the Hook
useEffect().

UseState()

State is used in our components whenever we need dynamic data in our applications. This is data whose values change over time. All the state for our applications is held in React's internal code. So when we have components that need to keep track of information between renderings, the component itself can create an initial state for itself, which can then be updated, and used. This is done by calling the useState Hook inside the component.

Import useState

To use the Hook, we must first import it at the very beginning of a component like so:

import React, { useState } from 'react';

function App() {
  return (
    <h1> My Favorite Coldplay Album </h1>
  )
}
Enter fullscreen mode Exit fullscreen mode

There are a two rules that come with using Hooks:
1) Only call Hooks at the top level : Never call Hooks inside conditional statements such as if/else, inside loops, or inside nested functions. This is because React relies on the order of the Hook calls.
2) Only call Hooks in React function components or from custom Hooks.

Call useState

After importing the Hook, we can now call the useState() Hook inside our function component.

import React, { useState } from 'react';

function App() {

const [album, setAlbum] = useState("")

  return (
    <h1> My Favorite Album </h1>
  )
}
Enter fullscreen mode Exit fullscreen mode

Calling useState: By calling useState(), React declares and initializes a state variable. This is how some values get preserved between function calls.

The useState argument: We can pass in an argument to useState of any primitive type that will be used as the initial state. In this case, we are setting the initial state as an empty string. It could easily be an array or a integer or an object too.

The useState return values: useState returns a pair of values (an array with two values), the current state, and a function that can be called to update the current state. (Updating a state variable will always replace it instead of merging it like with class components.) We used some array destructuring to access the first item of the pair and the second item of the pair and then assign them to our newly created const variables. In this case, they're album and setAlbum.

Every time we call the updater function setAlbum to change the state value, React replaces the state value of album with the value passed by the updater function setAlbum. React then knows the state value has changed so it must re-render the component. If the component calls useState for a second time, React will ignore the initial value argument and return the updated current state value and the updater function. The component can now use the updated state variable.

Read State

Here is an example of reading state in a component. The
album state variable can be used by the component like any other variable:

import React, { useState } from 'react';

function App() {

const [album, setAlbum] = useState("Viva La Vida")

  return (
    <h1> My Favorite Coldplay Album : {album} </h1>
  )
}
Enter fullscreen mode Exit fullscreen mode

Updating State

Here is an example of updating state in a component. After the user clicks the button "Parachutes", the state variable album will have a value of "Parachutes" when setAlbum("Parachutes") gets called. Since the state changed from "Viva La Vida" to "Parachutes", the component gets re-rendered:

import React, { useState } from 'react';

function App() {

const [album, setAlbum] = useState("Viva La Vida")

  return (
    <h1> My Favorite Coldplay Album : {album} </h1>
    <button type="button" onClick={() => setAlbum("Parachutes")}> 
     Parachutes
    </button>
  )
}
Enter fullscreen mode Exit fullscreen mode

UseEffect()

When a function is called and the function causes things to change outside of the function itself, it's considered to have caused a side effect.

Examples of side effects can be things like accessing data from a database, network requests, writing to a file system, or interacting with browser APIs. Function components are impure when their output gives unpredictable results which can happen if they reach outside of themselves. The useEffect Hook then lets us interact with the outside world but without affecting the rendering or performance of the component that it's in.

The UseEffect Hook is used to tell React that your component needs to run an "effect" or an additional set of instructions after every render or update to the DOM. When this happens React remembers the "effect" function within the useEffect hook and calls it later after rendering the DOM updates. The effect function still has access to the variables within the component because it's in the same scope.

As we saw before, a re-render of the component can happen when the state changes within a component. So the useEffect() hook by default will run every time the component gets rendered.

Import useEffect

To use the Hook, we must first import it at the very beginning of a component like so:

import React, { useEffect } from 'react';

function App() {
  return (
    <h1> My Favorite Coldplay Album </h1>
  )
}
Enter fullscreen mode Exit fullscreen mode

Call useEffect

After importing it, we can then call it inside our component. When calling it, we can pass it two arguments, an "effect" function, and a dependency array.

If no dependency array is passed in, the side effect function will run by default every time our component renders and can lead to an infinite loop. In other words, the effect will synchronize with all state.

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

If you pass in an empty array, the side effect function will run only the first time the component renders, when the component first mounts. In other words, the effect will synchronize with no state:

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

If the dependencies array has elements inside, then the side effect will run anytime those dependency variables change. React will re-run the effect even if just one of them is different from the last render. In other words, the effect will synchronize with the state of the dependencies. Always make sure the array includes all the values from the component (like props and state) that change over time and are used by the effect.

useEffect(() => {}, [variable1, variable2])
Enter fullscreen mode Exit fullscreen mode

Incorporating useEffect

In this example, I'm using useEffect to fetch some data from a database, then storing that fetched data in state for later use with the updater function. We use fetch inside useEffect so that in case the fetch request fails, we have a safe space to do so, and won't affect the rendering or performance of the component. I'm also using the empty dependencies array to run the effect only once after the first render. We're storing the fetched data in state so there's no need to fetch the data more than once:

import React, { useEffect } from 'react';

function App() {

const [albums, setAlbums] = useState([])

useEffect(() => {
    fetch("http://localhost:3001/albums")
      .then(res => res.json())
      .then(data => {
        setAlbums(data)
      })
  }, [])

  return (
    <h1> My Favorite Coldplay Albums </h1>
{
  )
}
Enter fullscreen mode Exit fullscreen mode

Optional useEffect cleanup function

The UseEffect Hook has an optional clean up for side effect functions that continue to run in the background when we no longer need them. We clean up in order to prevent unwanted behaviors such as memory leaks and also to optimize application performance. This can be implemented as a cleanup function that is returned by UseEffect.
The cleanup is called after the component re-renders as a result of setting state and before the useEffect callback is called. However, if the component is no longer being returned by its parent, then it becomes unmounted, and the cleanup happens at the end after the re-render.
This clean can range from clearing a timeout, removing listeners, etc.

This is how it looks:

useEffect(() => {
        //effect
        return () => {
            //cleanup
        }
    }, [])
Enter fullscreen mode Exit fullscreen mode

References:

ReactJs - Intro to Hooks
ReactJs - Hooks State
ReactJs - Hooks Effect

Top comments (0)