# useEffect

> The fewer raw useEffect calls you have in your components, the easier you will find to maintain your application.
>
> * <https://react.dev/learn/you-might-not-need-an-effect>

#### Side Effects

A side effect is a term in functional programming that refers to when a function relies on, or modifies, something outside its "responsibility" to do something. i.e Modifying a variable or any object outside the function's scope; Printing to the console; Making network requests; etc.

In the context of React, side effects include anything that isn't "predictable" or is external to React such as fetching data from an API, interacting with browser APIs, using setTimeout/setInterval, etc. React wants us to isolate such operations from the pure rendering of a functional component using the `useEffect` hook.

#### The `useEffect` hook

`useEffect` is a powerful but [often misused hook](https://react.dev/learn/you-might-not-need-an-effect).

This hook is typically used for:

* making API requests (without a library) for data that your app will display when it first mounts
* sending any "fire and forget" network requests like a POST request to an analytics service to log that a component was viewed
* to synchronise with things outside React: browser APIs like the DOM, WebGL, Canvas, Video, and Audio and third-party widgets/libraries like D3.js, Google Charts, Maps, etc.
* initialising a connection to a streaming/websocket server such as for a live chat component

`useEffect` can be used in three ways (*note the difference in the 2nd argument*):

**`useEffect(() => {}, [])`**

empty \[] means this will only run once when the Component mounts

**`useEffect(() => {}, [someValueToMonitor])`**

run on mount and then only if `someValueToMonitor` has changed

**`useEffect(() => {})`**

no 2nd argument means this will run on every render/rerender

#### APIs and `useEffect`

Fetching data from an API when a component mounts is one primary use case for `useEffect` if you're not using a library or an abstraction for data fetching and state management like [tanstack query](https://tanstack.com/query/latest) or [swr](https://swr.vercel.app/).

One important thing to note is that the callback function you pass to `useEffect` cannot return a `Promise` and therefore cannot be an `async` function. You can define and/or call an async function *inside* the callback though.

Don't do this:

```js
const [quotes, setQuotes] = useState([]);

useEffect(async () => {
  const res = await fetch('https://dummyjson.com/quotes');
  const { quotes } = await res.json();
  setQuotes(quotes);
}, []);
```

Do this:

```js
const [quotes, setQuotes] = useState([]);

useEffect(() => {
  const getQuotes = async () => {
    const res = await fetch('https://dummyjson.com/quotes');
    const { quotes } = await res.json();
    setQuotes(quotes);
  }

  getQuotes();
}, []);
```

#### Cleanup Function

The reason your `useEffect` callback can't return a `Promise` is that the hook expects a plain function as an optional return value. This returned function acts as a *cleanup* function and will be executed after a rerender and after the component unmounts. More [detail here](https://react.dev/reference/react/useEffect#parameters).

Let's say your app needs to know when the browser window is resized. The window is part of the DOM which is an external system to React so we add the event listener in a `useEffect`:

```js
useEffect(() => {
  const handleResize = () => {
    console.log("window resized");
  };
  window.addEventListener("resize", handleResize);
}, []);
```

The problem with the code above is that when the component the `useEffect` is in is umounted, the resize event listener will still be attached to the window and will keep firing. The implications of that depends on the application but can range from inefficient to disastrous. That's what the cleanup function is for.

Remove the resize event listener when the component is unmounted:

```js
useEffect(() => {
  const handleResize = () => {
    console.log("window resized");
  };
  window.addEventListener("resize", handleResize);

  return () => window.removeEventListener("resize", handleResize);
}, []);

```
