DEV Community

Cover image for Why React Hooks (Part I: Complicated lifecycles)
TrinhDinhHuy
TrinhDinhHuy

Posted on • Edited on

Why React Hooks (Part I: Complicated lifecycles)

Prerequisite: Basic knowledge about React

In this post, we are going to talk about one in million reasons why we should all start to learn and use Hooks even though fishing is not our hobby 🐟 🎣.

Hey Siri, set timer for 5 minutes πŸ˜‹

If you have never tried Hooks or heard of it before, stay with me for 5 mins and you can immediately recognize the first important benefit of Hooks πŸ₯‡.

I have started to use React hooks for about a month and it is a real game-changer. One clear benefit of Hooks is that it helps us remove redundant complicated React lifecycles.

Didn’t you see the no-fishing sign, son?

Me: I’m not fishing, sir. ....

😎 Let's how it works in action

Our task today is simply to subscribe the user to a radio channel πŸ“».

1. Class component

For this simple task, we will use the componentDidMount lifecycle

class Radio extends React.Component {
    state = {channelId: 1}

    componentDidMount() {
        subscribeToRadio(this.state.channelId)
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

One channel is kind of boring πŸ™ƒ

Let's allow users to jump to their favorite channels by clicking the button. For this, we need to unsubscribe from the previous channel before subscribing to the new one, componentDidUpdate should be the right place to do this

class Radio extends React.Component {

    ...

    componentDidUpdate(prevProps, prevState) {
        if (prevState.channelId !== this.state.channelId) {

            unsubscribeRadio(prevState.channelId)

            subscribeToRadio(this.state.channelId)
        }
    }

    changeChannel = () => {
        const id = randomId()
        this.state({channelId: id})
    }

    render() {

        return (
                <div>
                    <p>Current channel: ${channelId}</p>
                    <button onClick={this.changeChannel}>Change channel</button>
                </div>
            )
    }
}
Enter fullscreen mode Exit fullscreen mode

Last but not least, we have to unsubscribe to the channel when the user stops listening. We will do this in componentWillUnmount

class Radio extends React.Component {

    ...

    componentWillUnmount() {
        unsubscribeRadio(this.state.channelId)
    }
}
Enter fullscreen mode Exit fullscreen mode

So, for this dead simple radio subscribing task, we still need 3 lifecycles in total:

  • componentDidMount
  • componentDidUpdate
  • componentWillUpdate

If you add more and more features, I assume it has some side-effects, to your app, these features would be grouped by lifecycle methods instead of by side-effect. You will end up stacking up logics all across these lifecycle methods.

🀨 🧐 πŸ˜•

Imagining when you have a bug πŸ›, you need to go through 3 different places to find and fix it. You have to play Three-bug Monte game to
find that bug and I bet you all know how difficult it is to win that kind of game πŸ€¦β€

bugs

2. Function component with Hooks

πŸ”₯ Hooks come to rescue

Hooks are a new addition in React 16.8. They let you use state and other React features without writing a class.

Let's see how Hook does to achieve the same result. To use Hooks, we need to convert the class component above to a functional component. We can create a super simple component in the following way:

const Radio = () => {
    const [channelId, setChannelId] = React.useState(1)

    React.useEffect(() => {
        subscribeToRadio(channelId)

        return () => unsubscribeRadio(channelId) 
    }, [channelId])

    const changeChannel = () => {
           const id = randomId()
           setChannelId(id)
    }

    return (
        <div>
            <p>Current channel: ${channelId}</p>
            <button onClick={changeChannel}>Change channel</button>
        </div>
    )

}
Enter fullscreen mode Exit fullscreen mode

😲 Wow, It's magic. How do you do that?

React.useState and React.useEffect are React Hooks that helps you achieve the same results as you did with lifecycles. Even though you might not totally clear on those above Hooks, I bet the function names might give you some hints, and you can still immediately feel that Hooks make the code much cleaner and simpler.

As you can see, all the logics are concentrated in 1 Hook instead of 3 places like before. It is easier to debug. If you want to remove the feature, all you need is to delete the related effect.

πŸ’ͺ Let's break it down, together.

β€’ useState

useState is a Hook that helps us manage local state in function component.

const Radio = () => {
    const [channelId, setChannelId] = React.useState(1)

    const changeChannel = () => {
        const id = randomId()
        setChannelId(id)
    }

    ...
}
Enter fullscreen mode Exit fullscreen mode

The useState Hook accepts initial state as its argument. In the code above, 1 is the initial value for channelId.

This Hook returns an array which contains 2 variables where the first one is the current state and the second one is a function that allows us to update state. We are using array destructuring [channelId, setChannelId] and you can name them whatever you want

β€’ useEffect

React.useEffect lets us perform side effects in function component

const Radio = () => {

    const [channelId, setChannelId] = React.useState(1)

    React.useEffect(() => {
        subscribeToRadio(channelId)
    }) 

    ...   
}
Enter fullscreen mode Exit fullscreen mode

In this effect, we perform the radio channel subscription. By default, our effect will run after every component's rendering and updating.

However, that's actually not what we want, if we only need to perform this effect only once after the first render (componentDidMount), we need to pass an empty array as a second optional argument of useEffect Hook. An empty array means that this effect depends on nothing, so it will only run on the mount (and unmount if you return a clean-up function)

const Radio = () => {

    const [channelId, setChannelId] = React.useState(1)

    React.useEffect(() => {
        subscribeToRadio(channelId)
    }, []) 

    ...   
}
Enter fullscreen mode Exit fullscreen mode

Additionally, we also need to perform the effect after every time the channelId state changes (when the user clicks the button). We will tell the effect to do that by passing channelId to the array. Remember what you put here in the array is what the effect will depend on. The good news is, you can put multiple variables to this array!!

const Radio = () => {

    const [channelId, setChannelId] = React.useState(1)

    React.useEffect(() => {
        subscribeToRadio(channelId)
    }, [channelId]) 

    ...   
}
Enter fullscreen mode Exit fullscreen mode

The effect will determine when channelId changes by comparing the current channelId value with the previous channelId value (using shallow comparison ===).

If those values are unchanged, React would skip the effect πŸ˜™. It's the same when we use componentDidUpdate lifecycle to compare
this.state.channelId and prev.state.channelId

Note: It uses SHALLOW equality instead of deep comparison

Lastly, we will unsubscribe to πŸ“» when user changes the channel

const Radio = () => {

    const [channelId, setChannelId] = React.useState(1)

    React.useEffect(() => {
        subscribeToRadio(channelId)

        return () => unsubscribeRadio(channelId) 

    }, [channelId]) 

    ...   
}
Enter fullscreen mode Exit fullscreen mode

The effect returns a function known as the clean-up function.

When using class component, we have to split this logic to 2 components componentDidUpdate and componentWillUnmount

Our effect will not only run when first render but also every update (when channelId changes), therefore, the clean-up function will be called
each time before the next render

3. Conclusion:

One benefit of Hooks is that it makes your React app 90% cleaner by removing the complicated lifecycles.

It helps us keep things together, group our app features by side-effect rather than spread them into different lifecycle methods

Feel the need for learning Hooks now, here are some good resources for you:

πŸ™ πŸ’ͺ Thanks for reading! Brace yourself, part II is coming

Please leave your comments below to let me know what do you think about React Hooks

✍️ Written by

Huy Trinh πŸ”₯ 🎩 β™₯️ ♠️ ♦️ ♣️ πŸ€“

Software developer | Magic lover

Say Hello πŸ‘‹ on

βœ… Github

βœ… LinkedIn

βœ… Medium

Top comments (0)