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

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

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

...

} ```

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

```jsx 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>
        )
}

} ```

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

```jsx class Radio extends React.Component {

...

componentWillUnmount() {
    unsubscribeRadio(this.state.channelId)
}

} ```

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:

```jsx 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>
)

} ```

๐Ÿ˜ฒ 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.

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

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

...

} ```

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

```jsx const Radio = () => {

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

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

...

} ```

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)

```jsx const Radio = () => {

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

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

...

} ```

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!!

```jsx const Radio = () => {

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

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

...

} ```

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

```jsx const Radio = () => {

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

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

    return () => unsubscribeRadio(channelId)

}, [channelId])

...

} ```

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: - Hooks Intro - Modern React workshop by Kent C. Dodds (Part I) - Modern React workshop by Kent C. Dodds (Part II)

๐Ÿ™ ๐Ÿ’ช 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