DEV Community

Cover image for React Hooks
Husnain Mustafa
Husnain Mustafa

Posted on

React Hooks

What are hooks?

Hooks are the functions which provides you to access capabilities of class components in your functional components.
For example, in class component you can make a component stateful, which is not possible in functional components without hooks.
Furthermore, you can define the actions which are required to be fired at specific point in the lifecycle of component. This was only possible in class components, and not in functional component, until hooks were introduced

Hooks were introduced in React 16.8.

Hooks provided by React

There are many many hooks that are provided by react. Some of them are:

  • useEffect

  • useState

  • useRef

  • useMemo

  • useCallback

  • useContext

  • useReducer

  • useLayoutEffect

For this tutorial, we are going through only the first five (useEffect, useState, useRef, useMemo, useCallback)

useEffect

useEffect lets you fire a desired action, i.e code execution, at some specific stages of component lifecycle.

But why would we need to fire a desired action at some specific stages of component lifecycle?

Example of firing an action

Well, let's consider a situation where you and your colleague are working on a project, lets say an eCommerce website. Your colleague is working on the front end and make a button which, when clicked, opens a Paypal dialog where user enters credentials and place an order. But the problem is, your colleague does not know how to integrate Paypal's api with eCommerce website. So he asks you to integrate Paypal's api. Along this, he also provides you a function, in which you are required to do all the tasks related to integrating Paypal's api.

To understand useEffect, we first need to understand the lifecycle of component in React.
There are 3 stages of component lifecycle which are as follows:

  • Component Mount, i.e the component gets rendered.

  • Component Update, i.e the component gets updated.

  • Component Unmount, i.e the component gets removed from the DOM.

We can fire an action at any of these 3 stages of lifecycle through useEffect.

How to implement useEffect

UseEffect takes 2 parameter, first one is the function which will get fired, and second one is the dependency array. Dependency array consists of variables which, when gets updated, causes our function to get fired. You will understand better by going through the codes below:

  • Component Mount:
....
....

function myComponent(){
    useEffect(()=>{
        //Fire an action
    }, [])
}

....
....
Enter fullscreen mode Exit fullscreen mode

You can see the we left the second argument as an empty array. When we keep the dependency array (second argument) empty, we let React know that this we need to fire our desired action, function provided as first argument, when our components get mounted upon.
This case is same as below code in class component:

....
....

class myComponent extends Component{
    componentDidMount(){
        //Fire an action
    }
}

....
....
Enter fullscreen mode Exit fullscreen mode

Let's move on to second case, i.e, Component Update:

....
....
function myComponent(){
    useEffect(()=>{
        //Fire an action
    }, [variable_1, variable_2])
}
....
....
Enter fullscreen mode Exit fullscreen mode

In this case, we can see that code is almost same as before, but we have given 2 variables, variable_1 and variable_2, as dependency array. This lets React know that we want to fire our desired action whenever react find a change in any of these 2 variables. These variables are mostly the states of the components which will be explained later.

Class Component will implement same thing by:

....
....

class myComponent extends Component{
    componentDidUpdate(){
        //Fire an action
    }
}

....
....
Enter fullscreen mode Exit fullscreen mode

Let's move on to third case, i.e, Component Unmount:

....
....
function myComponent(){
    useEffect(()=>{
        return ()=>{
            //Fire an action
        }
    }, [])
}
....
....
Enter fullscreen mode Exit fullscreen mode

In this case, we can see that code is almost same as case one, but instead of firing our desired action in the function provided as first component, we are firing our action in the function that we are returning.

Class Component will implement same thing by:

....
....

class myComponent extends Component{
    componentDidUnmount(){
        //Fire an action
    }
}

....
....
Enter fullscreen mode Exit fullscreen mode

We can also have a combination of mounting and unmounting action in the same useEffect by:

....
....
function myComponent(){
    useEffect(()=>{
        //Firing an action at mounting

        return ()=>{
            //Fire an action at unmounting
        }
    }, [])
}
....
....
Enter fullscreen mode Exit fullscreen mode

useState

States are use to store the data which can be used over time and can be updated as desired.

How are states different than variables?

General variables in react, like let myvar = 10, get re initialized whenever a component re renders. Components get re renders whenever its state or props gets changed.

So let's consider we have parent component which passes prop, 'color', to the child component. This 'color' is used by child as font color. And we got a button with label, 'change color' which changes color to random one.

And in child component we, we need to keep the track about how many times color got changed, i.e, user clicked the 'change color' button.

If we use general variable like, let count = 0 and increment it every time user click on the 'change color', count will always be zero. Why? because every time user clicks 'change color', our component get re renders, as prop 'color' got change. So how can we store the count? We will use the state. States keep the data stored even if component gets re rendered.

How to implement useState

useState is easy to understand and use. useState is a function which takes one argument, the initial value of state, and return an array, which has state value at first index and a setter function to update the state value at second index. We do not update the value of state directly, if we do so, React does not consider that update and hence does not re renders the component. So we update the state value through the setter function provided by useState. Below is the code to understand it better:

....
....

const [count, setCount] = useSate(0)

....
....
Enter fullscreen mode Exit fullscreen mode

useRef

useRef is somewhat same as useState, but instead of storing the value, it stores the reference of the element in the DOM. It is useful in making uncontrolled inputs, explained later.
Below is the code to implement useRef:

....
....

const ageRef = useRef(null)

....
....

return(
    ... 
    <input ref = {ageRef} />
    ...
)

....
....
Enter fullscreen mode Exit fullscreen mode

You can fetch the element anywhere in the component by:
ageRef.current

What are controlled and uncontrolled inputs

Controlled inputs are the ones whose values are 'controlled' by the React states. We assign the value of input as the state and we update the state in the onChange method call. Below code would help you understand better.

....

const [age, setAge] = useState(null)

...

<input value = {age} onChange = {(event)=>{event.target.value}} />

...
....

Enter fullscreen mode Exit fullscreen mode

Controlled inputs are better when we need to validate our input or when our input can cause the changes in DOM, for example, when typing age input as 13, we show the user the balloons on the screen.

Uncontrolled Inputs are inputs when we do not assign the value of input as state. Here we usually implement useRef to fetch the value of input.

....

const age = useRef(null)

...

<input ref = {age}/>

...
....

Enter fullscreen mode Exit fullscreen mode

We can retrieve the value of input by:
age.current.value

useMemo

useMemo is used to store the returned value of the function. It is useful when got a high computational function. So if our component gets re rendered, we do not need to re call this function as we already have the returned value stored, unless variables in dependency array changes.

....
....

const age = useMemo(()=>{
    //Doing some expensive function
    ....
    ....
    return age
}, [var_1, var_2])

....
....
Enter fullscreen mode Exit fullscreen mode

useCallback

useCallback is similar to useMemo, but instead of storing the value returned by function, we store the function itself, so that it does not get re defined/re initialized when the component gets re rendered.
It is useful in a case when we pass the function to the child component as prop. So whenever the parent component get re rendered, function get re defined/re initialized, reference to the function got changed, hence child also gets re rendered.

To avoid this, we can implement useCallback which stores the function and do not re initialize it unless the variables in dependency array gets changed.

Below is the code for implementing useCallback:

....
....

const calculateAge = useCallback(()=>{
    //Tasks to calculate age
}, [var_1, var_2])

....
....
Enter fullscreen mode Exit fullscreen mode

Both useMemo and useCallback can help optimize the performance of your React components by memorizing values or functions, respectively, and preventing unnecessary re renders.

That's all. Hope it would help.

Top comments (0)