React.useEffect hook explained with practical examples
Tags: react, webdev, beginners
Published: 2022-02-17 13:44:20

**useEffect hook** are very useful to manage **side effects** and to control **lifecycle** methods. However, many subtleties in its implementation can cause unexpected behavior, causing all kinds of bugs. 🐛
<br/><br/>

## Lifecycle definition 📖📖📖

The life cycle is defined as the behavior of a component while it exists. 

1. **Mounting** : React.Element returned by the component is injected for the first time

2. **Updating** : React.Element is updated according to the value of the shouldComponentUpdate() lifecycle method

3. **Unmounting** : React.Element is removed from the DOM 
<br/><br/><br/>

## Usage and definition ⚙️⚙️⚙️

To use useEffect, we need to import it into our file from React. 

Then we can use it in a function component or in a custom hook. Let's use it in the first way. useEffect takes as parameters a callback and an array of dependencies. 

```javascript
import React, { useEffect } from "react"

function Effect() {
    useEffect(() => {
        console.log("I'm called on mounting")
    }, []);

    return (
        <div></div>
    )
}
```

### componentDidMount()/componentWillUnmount() 

useEffect with an empty dependency array and a callback that returns a cleanup function is like using componentDidMount() and componentWillUnmount() in this way.

Cleanup function can be anonymous function as well.

```javascript
componentDidMount() {
    console.log("Called on mounting");
}

componentWillUnmount() {
    console.log("Called on unmounting");
}
```
```javascript
useEffect(() => {
    console.log("Called on mounting");

    return function cleanup() {
        console.log("Called on unmounting");
    }
}, [])
```

### componentDidMount()/componentDidUpdate() with dependency check 

useEffect with an array of dependencies is the same as using componentDidMount() and componentDidUpdate() together with a props and state comparison.

```javascript
componentDidUpdate(prevProps, prevState, snapshot) {
    if (prevState.number === this.state.number)
        return;
    console.log("Called when number state change");
}

componentDidMount() {
    console.log("Called when number state change");
}
```

```javascript
useEffect(() => {
    console.log("Called when number state change")
}, [number])
```

### componentDidMount()/componentDidUpdate without dependency check

useEffect without an array of dependencies is like using componentDidMount() and componentDidUpdate() together without props and state comparison

```javascript
componentDidUpdate(prevProps, prevState, snapshot) {
    console.log("Called when number state change");
}

componentDidMount() {
    console.log("Called when number state change");
}
```
```javascript
useEffect(() => {
    console.log("Called when number state change")
})
```

### componentDidUpdate() only

useEffect is not designed to handle componentDidUpdate() only. 

You must check that the component has already been rendered with a reference. A custom hook is made for that, here it is

```javascript
componentDidUpdate(prevProps, prevState, snapshot) {
    if (prevState.number === this.state.number)
        return;

    console.log("Called when number state change");
}
```
```javascript
function useUpdateEffect(cb, deps) {
    const mountedRef = useRef(false);

    useEffect(() => {
        if (!mountedRef.current) {
            mountedRef.current = true;
            return;
        }

        cb();
    }, deps);
}
```
<br/><br/>

## Usage 1 : useEffect to subscribe/unsubscribe to API 👨‍💻

Most of the time you have to subscribe to services to benefit from features. 

Here we use the browser's setInterval API to manage a timer that updates every second.

The callback passed as parameter to useEffect is executed when the component is mounted, setInterval is launched at this time. 

When the component is unmounted, we clean up our interval so that it does not run anymore. 

```javascript
function Timer() {
    const [date, setDate] = useState(null);
    const idRef = useRef(null);

    useEffect(() => {
        idRef.current = window.setInterval(() => {
            setDate(new Date().toLocaleTimeString());
        }, 1000);

        return function cleanup() => {
            window.clearInterval(idRef.current);
        }
    }, []);

    return (
        <div>
            {date}
        </div>
    );
}
```
<br/><br/>

## Usage 2 : useEffect to manage side effects when modifying local state. 👨‍💻👨‍💻👨‍💻

Side effects are common in React applications. 

Here, we useEffect to verify that the number entered by the user is a number.

We perform this operation at the mounting and updating phases

```javascript
function NumberInput() {
    const [ number, setNumber ] = useState('');
    const [ error, setError ] = useState(false); 

    const handleChange = useCallback(e => {
        const { value } = e.target;
        setNumber(value);
    }, []);

    const check = useCallback(number => {
        return number && Number.parseInt(number, 10);
    }, [number]);

    useEffect(() => {
        const checked = check(number);
        setError(!checked);
    }, [number])

    return (
        <div>
            <input type="text" value={number} onChange={handleChange} />
            {error ? `Please enter a number` : `You entered ${number}`}
        </div>
    )
}
```
<br/><br/>

## 6.0 Pros and cons 

useEffect is not a perfect tool and is not intended to completely replace life cycle management with Class Component

### 6.0.1 useEffect pros 

- Write more concise code ✔️
- Separate concerns by using multiple useEffect in the same component ✔️
- Extract logic from useEffect ✔️

### 6.0.2 useEffect cons 

- Don't have full control over the life cycle of the component ❌
- There are subtleties to take into account that complicate the development a bit ❌

### 6.0.3 Lifecycle Class Component pros
- Full lifecycle control ✔️
- No ambiguity regarding behavior ✔️

### 6.0.4 Lifecycle Class Component cons
- Much more verbose code ❌
- Repeated code in different lifecycle functions. ❌
- One must use a HOC to separate rendering logic from lifecycle and state management logic, which can be quite verbose ❌
<br/><br/>

**Thank you for reading.**

I hope you have learned a lot from this article and that like me, your mastery of front-end development has improved.

Do not hesitate to correct errors or ask me questions in the comments, I will respond to them as soon as possible

If you like my content and want to support me, don't hesitate : 

1. Subscribe to my [Github](https://github.com/crunchstack), I push code almost every day

2. Subscribe to my [Frontend mentor](https://www.frontendmentor.io/profile/crunchstack) profile, I make challenges and I'm quite active in the community