Awesome React-Hooks - Part 2 - useEffect
Tags: react, reacthooks
Published: 2018-11-01 18:33:34

---
title: Awesome React-Hooks - Part 2 - useEffect
published: true
description: Learn how to use the upcoming useEffect hook in react
tags: react, react-hooks
cover_image: https://images.unsplash.com/photo-1525677277716-c0e820ed2829?ixlib=rb-0.3.5&ixid=eyJhcHBfaWQiOjEyMDd9&s=8735e06d37902b7bc19921dae966bb10&auto=format&fit=crop&w=1350&q=80
---

> This post is the second in a series about React hooks.

Part 1 : {% link https://dev.to/tclain/awesome-react-hooks-part-1-usestate--11n6 %}

In the last post, we saw how to use the `useState` hook in React 16.7+. Today let's learn about `useEffect`


# TLDR

`useEffect` take a `callback` function as arguments that will be re-run **after each rerender of your functional component**.

If this callback returns another function, this function will be called on the component unmount.

`useEffect`, can take a second arguments: `any[]`, that is a list of dependencies that should trigger a rewrite. These dependencies can be a prop, or another state produced by `setState`.

## Example with a persisted counter

Let's say we take our counter increment as before and we want the value to be persisted in localStorage,

we could use `useEfffect` for this.

As a reminder, here is our base code:


```js

import React, {useState, useEffect} from 'react'
function Counter() {
  const [counter, setCounter] = useState(0)
    // persist logic will be here
  return {
    counter, 
    setCounter
  }
} 

export const App = () => {
   const {setCounter, counter} = Counter();
  return <button onClick={() => setCounter(counter + 1)}>Change {counter}</button>
}
```

The `Counter` function define a useState hook to store our counter value.

If we are defining :

```js
 useEffect(() => {
      window.localStorage.setItem('counter', counter)
  })
```

The `setItem` operation will be run **after** each rerender.


We have one step left, to populate the counter value with the value from localStorage for the first time.


```js
  const [counter, setCounter] = useState(JSON.parse(window.localStorage.getItem('counter')) || 0)
```

The whole example can be found here:

https://stackblitz.com/edit/react-use-effect-hook?file=index.js

## Cleanup function

If you return a function from the useEffect callback, this function will be cleaned. This is super useful if you need to unsubscribe from global events ... and so on.

## Practical Example

Let say we got an async search box that displays the user list from GitHub.

We could use the combination of `useState` and `useEffect` to fetch dynamically the list from the query entered by the user.

As we did it before, let's create a custom hook function.

```js

function useGithubUsers() {
  const [query, setQuery] = useState("")
  const [results, setResults] = useState([])
  const [loading, setLoading] = useState(true)
// side effect here
    // exposing only public api !
    return {
        query, setQuery, results, loading    
    }  
}

```

So we are basically declaring three variables: `query` (the current query search 
), `results` (an array of Github users), `loading` (a loading indicator).

Here how we could use this custom hook:

```js
export const App = () => {
  const { setQuery, query, results, loading } = useGithubUsers();
  return <div>
    <input onChange={e => setQuery(e.target.value)} />
    <ul>
      {loading && <li>Loading</li>}
      {
        results && results.map((item, index) => <li key={index} >{item.login}</li>)
      }
    </ul>
  </div>
}
```

What's cool with hooks, we can reason very easily about the lifecycle of our data.

Here, if we insert the useEffect Hook between `useState` declarations and the return function, the side effect after all `state` will be changed and the component rerender.

Let's fetch (pun intended) the GitHub users using the `search` API from github.com.

```js
 if (query !== "") {
      setLoading(true);
      fetch(`https://api.github.com/search/users?q=${query}`, { method: "GET"}).then(req => {
        return req.json();
      }).then(data => {
        setLoading(false)
        setResults(data.items)
      })
    }

```

If you would run this code directly, you would have a big problem, cause the useEffect is rerun after each rerender (aka infinite loop in this case), so you'll need to use the second argument of the `useEffect` function that takes an array of variables that need to change to run this effect (__a la shouldComponentUpdate__)

```
    setEffect( () => {...}, [query])
```

> If you provide an empty array, any code inside the effect would run only once when the component mounts, and any code inside the `dispose` returned function would run only on the unmount of the component.
By the way, you could use a prop here, giving you a neat way of reacting to props change!

The resulting code is :

```js
import React, { useState, useEffect } from 'react'
import { render } from 'react-dom'
import Select from 'react-select';
import debounce from 'lodash.debounce';


function useGithubUsers() {
  const [query, setQuery] = useState("")
  const [results, setResults] = useState([])
  const [loading, setLoading] = useState(false)
  // each rerender
  useEffect(debounce(() => {
    if (query !== "") {
      setLoading(true);
      fetch(`https://api.github.com/search/users?q=${query}`, { method: "GET"}).then(req => {
        return req.json();
      }).then(data => {
        setLoading(false)
        setResults(data.items)
      })
    }
  }, 300), [query])
  return {
    query,
    setQuery,
    setLoading,
    results,
    loading,
  }
}

export const App = () => {
  const { setQuery, query, results, loading } = useGithubUsers();
  return <div>
    <input onChange={e => setQuery(e.target.value)} />
    <ul>
      {loading && <li>Loading</li>}
      {
        results && results.map((item, index) => <li key={index} >{item.login}</li>)
      }
    </ul>
  </div>
}


render(<App />, document.getElementById('root'));

```

You can test it live here:

https://stackblitz.com/edit/react-use-effect-hook-github?

> Github has some rate limit on this API so if it does not work, wait a minute and retry.


Of course, this example is trivial and very basic.
Nothing prevents you to abstract a little more and reuse it for AJAX Requests :-)


## Next

In the next post, we'll see how to optimize this logic by using the memoization hooks.