React Hook: Using Memo() - Callback() - Context() - State()

React Hook: Using Memo() - Callback() - Context() - State()

In this article, we discuss learned about useMemo() Hook, useCallback() Hook, useContext() Hook, useState() Hook in ReactJS and how it can be used in the application.

Introduction

In this article, we discuss learned about useMemo() Hook, useCallback() Hook, useContext() Hook, useState() Hook in ReactJS and how it can be used in the application.

1. useMemo() Hook

The useMemo() hook allows you to memoize functions so that the function is prevented from being called on every render. It just includes a function containing an array of inputs which helps useMemo() hook to only recompute the memorized value when any of the input arrays have been changed.

Unlike useEffect() hook, the useMemo() hook does not trigger every time you change one of its dependencies.

The memoized function will always check to see if the dependencies need to be changed since the last render. If so, it executes the function and returns the result. And if it returns false, which dictates that nothing is changed, so it will return the cached result from the last execution. So, in this way useMemo() hook helps to improve performance in React application.

Syntax

const memoizedValue = useMemo(() => computefunction(a, b), [a, b]);  

Where a,b can be values that need to be processed and a and b in square brackets are to specify when function need to be processed.

useMemo() hook memoize takes a function that needs to be memoized and an array of values that when changed, would invalidate the memoization.

Now let’s look at the demo and how it is used for performance optimization.

Create a component named RandomWords which will return words randomly on the click of a button,

import React, { useState } from 'react'  
  
function RandomWords() {  
    const [count, setCount] = useState(0)  
    const [wordIndex, setWordIndex] = useState(0)  
  
    const words = ['This', 'is', 'React', 'Application', 'for', 'Testing', 'By', 'Priyanka']  
    const word = words[wordIndex]  
  
    const computeLetterCount = (word) => {  
        let i = 0;  
        while (i < 200000000) i++  
        console.log(i)  
        return word.length;  
    };  
  
    const letterCount = (word) => { computeLetterCount(word) };  
    return (  
        <div>  
            <div style={{ padding: '15px' }}>  
                <h2>Compute number of letters (slow)</h2>  
                <p>"{word}" has {letterCount(word)} letters</p>  
                <button  
                    onClick={() => {  
                        const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1;  
                        setWordIndex(next);  
                    }}  
                >  
                    Next word  
                </button>  
  
                <h2>Increment a counter (fast)</h2>  
                <p>Counter: {count}</p>  
                <button onClick={() => setCount(count + 1)}>Increment</button>  
            </div>  
        </div>  
    )  
}  
  
export default RandomWords  

The output will be displayed as below,

As we see that on click of any button all methods are rendered which reduces performance.

So here we will make use of useMemo() hook,

import React, { useState,useMemo } from 'react'  
  
function RandomWords() {  
    const [count, setCount] = useState(0)  
    const [wordIndex, setWordIndex] = useState(0)  
  
    const words = ['This', 'is', 'React', 'Application', 'for', 'Testing', 'By', 'Priyanka']  
    const word = words[wordIndex]  
  
    const computeLetterCount = (word) => {  
        let i = 0;  
        while (i < 200000000) i++  
        console.log(i)  
        return word.length;  
    };  
  
    const letterCount = useMemo(() => computeLetterCount(word), [word]);  
  
    return (  
        <div>  
            <div style={{ padding: '15px' }}>  
                <h2>Compute number of letters (slow)</h2>  
                <p>"{word}" has {letterCount} letters</p>  
                <button  
                    onClick={() => {  
                        const next = wordIndex + 1 === words.length ? 0 : wordIndex + 1;  
                        setWordIndex(next);  
                    }}  
                >  
                    Next word  
                </button>  
  
                <h2>Increment a counter (fast)</h2>  
                <p>Counter: {count}</p>  
                <button onClick={() => setCount(count + 1)}>Increment</button>  
            </div>  
        </div>  
    )  
}  
  
export default RandomWords   

Now, the output we observe will be quite fast and it will only return delayed output for the first button rather than the second button.

So, the useMemo() hook should be used in the case where transforming API, fetching data from API, or any other operation where there are multiple operations going on a single page.

2. useCallback() Hook

In React, useCallback() hook is another important hook which is used for performance optimization. When we implement or call any component, all components re-render every time any call is made. For a small application, it will not have much effect. But when dealing with a large application, it will give performance issues. So, useCallback() provides a way to render components only when required.

Let’s look at the demo.

Create 4 components.

Title.js

import React from 'react'  
  
function Title() {  
    console.log("Title component rendered")  
    return (  
  
        <div>  
            React Title  
        </div>  
    )  
}  
  
export default Title   

Button.js

import React from 'react'  
  
function Button({count,handleClick}) {  
  
    console.log("Button Component rendered")  
    return (  
        <div>  
            <p>Counter : {count}</p>  
            <button onClick={handleClick}>Increment Counter</button>  
        </div>  
    )  
}  
  
export default Button   

Textbox.js

import React from 'react'  
  
function Textbox({name,handleClick}) {  
    console.log("Textbox Rendered")  
    return (  
        <div>  
            <input type="text" placeholder="Enter Name" value={name} onChange={handleClick}/>  
        </div>  
    )  
}  
  
export default Textbox   

Parent.js

import React, { useState } from 'react';  
import Title from './components/Title';  
import Button from './components/Button';  
import Textbox from './components/Textbox';  
  
function Parent() {  
  
  const [count, setCount] = useState(0)  
  const [name, setName] = useState("")  
  
  const incrementCounter = () => {  
    setCount(count + 1)  
  }  
  
  const updateName = (e) => {  
    setName(e.target.value)  
  }  
  
  return (  
    <div className="App">  
      <Title />  
  
      <Button count={count} handleClick={incrementCounter} />  
        
      <label>Name is {name}</label>  
      <Textbox text={name} handleClick={updateName} />  
    </div>  
  );  
}  
  
export default Parent;   

The output will be displayed as below.

Initially, it will render as below.

On click of a button, we can check in the console that all components are rendered again even if there is the change in a single component.

Now, the same - even if a change in a textbox is made, all components are rendered again.

So, to avoid the rerendering of components, there is a need for useCallback().

Now, while the exporting component adds React.Memo() function, it will render only if there will be a change in props or state.

Title.js

import React from 'react'  
  
function Title() {  
    console.log("Title component rendered")  
    return (  
  
        <div>  
            React Title  
        </div>  
    )  
}  
  
export default React.memo(Title)  

Button.js

import React from 'react'  
  
function Button({count,handleClick}) {  
  
    console.log("Button Component rendered")  
    return (  
        <div>  
            <p>Counter : {count}</p>  
            <button onClick={handleClick}>Increment Counter</button>  
        </div>  
    )  
}  
  
export default React.memo(Button)  

Textbox.js

import React from 'react'  
  
function Textbox({name,handleClick}) {  
    console.log("Textbox Rendered")  
    return (  
        <div>  
            <input type="text" placeholder="Enter Name" value={name} onChange={handleClick}/>  
        </div>  
    )  
}  
  
export default React.memo(Textbox)  

Now, check the output. Initially, it will render all 3 components.

After clicking on the button, the Title component will not be rendered.

So, to resolve the issue of the re-rendering components, if there is no change in the component useCallback() hook is used.

useCallback() hook will return a memorized version of the callback function that will change only when dependencies have changed. It is useful when passing callbacks to optimized child components that rely on reference equality to prevent unnecessary renders.

Now add callback function in Parent.js

import React, { useState,useCallback } from 'react'  
import Title from './Title';  
import Button from './Button';  
import Textbox from './Textbox';  
  
function Parent() {  
    const [count, setCount] = useState(0)  
    const [name, setName] = useState("")  
    
    const incrementCounter = useCallback(() => {  
      setCount(count + 1)  
    },[count])  
    
    const updateName = useCallback((e) => {  
      setName(e.target.value)  
    },[name])  
    
    return (  
        <div>  
            <Title />  
            
            <Button count={count} handleClick={incrementCounter} />  
            <label>Name is {name}</label>  
            <Textbox text={name} handleClick={updateName} />  
        </div>  
    )  
}  
  
export default Parent  

The output will be displayed as below.

Now, on click of a button, only the button component is rendered.

Now, on typing in textbox, the textbox component is rendered.

So, this way, we can optimize performance.

useCallback() hook provides an efficient way to write code and organize components by rendering a component only when it is required which also provide performance improvement and code optimization. It is used with React.memo() which makes sure that the no extra render should be performed unnecessarily. So, this way the complete flow works.

When to use useCallback() and useMemo()?

There are 2 main reasons why both hooks are built into React,

  1. Referential equality
  2. Computationally expensive calculations.

Difference Between useMemo() and useCallback() hook?

3. useContext() Hook

What is React Context

In React, suppose we have parent component  having 3 levels of child components,

Like the above image, if we want to pass any data from parent component to child component C, so as per React we need to pass props to each level to be available to Child component c. This solution may be feasible when a number of child components are many. So, React hooks provides a concept call Context.

React Context API allows you to easily access data at different levels of the component tree, without passing prop to every level as we have seen in the previous series by creating context, now in this, it will be achieved using useContext() hook.

useContext() hook

This hook is used to pass data from one component to another without being specified to each of the component trees.

Let’s look at the demo,

Create a hierarchy as per the above image.

Create 4 functional components,

ChildC.js

import React from 'react'  
  
function ChildC() {  
    return (  
        <div>  
            Child Component at level 3  
        </div>  
    )  
}  
  
export default ChildC  

ChildB.js

import React from 'react'  
import ChildC from './ChildC'  
  
function ChildB() {  
    return (  
        <div>  
            <ChildC/>  
            Child Component at level 2  
        </div>  
    )  
}  
  
export default ChildB  

ChildA.js

import React from 'react'  
import ChildB from './ChildB'  
  
function ChildA() {  
    return (  
        <div>  
            <ChildB/>  
        Child Component at level 1  
        </div>  
    )  
}  
  
export default ChildA   

ParentComponent.js

import React from 'react'  
import ChildA from './ChildA'  
  
function ParentComponent() {  
    return (  
        <div>  
            <ChildA/>              
        </div>  
    )  
}  
  
export default ParentComponent 

and import in App.js

import React from 'react';  
import './App.css';  
import ParentComponent from './components/ParentComponent';  
  
function App() {  
  return (  
    <div className="App">  
      <ParentComponent/>  
    </div>  
  );  
}  
  
export default App;  

Now to create we have 3 steps,

First, create a context in App.js.

Provide value using .

Use value of context using .

Create context object and pass value in App.js as below.

import React from 'react';  
import './App.css';  
import ParentComponent from './components/ParentComponent';  
  
export const UserContext = React.createContext()  
  
function App() {  
  return (  
    <div className="App">  
      <UserContext.Provider value={'ReactApp'} >  
      <ParentComponent/>  
      </UserContext.Provider>  
    </div>  
  );  
}  
  
export default App;  

and consume value in ChildC.js

import React from 'react'  
import {UserContext} from '../App'  
  
function ChildC() {  
    return (  
        <div>  
            Child Component at level 3  
            <UserContext.Consumer>  
                {  
                    user => {  
                        return <div>Context value is {user}</div>  
                    }  
                }  
            </UserContext.Consumer>  
        </div>  
    )  
}  
  
export default ChildC  

The output will display as below.

Now, let’s see how multiple values can be consumed using context.

import React from 'react';  
import './App.css';  
import ParentComponent from './components/ParentComponent';  
  
export const UserContext = React.createContext()  
export const CategoryContext = React.createContext()  
  
function App() {  
  return (  
    <div className="App">  
      <UserContext.Provider value={'ReactApp'} >  
        <CategoryContext.Provider value={'Programming'}>  
          <ParentComponent />  
        </CategoryContext.Provider>  
      </UserContext.Provider>  
    </div>  
  );  
}  
  
export default App;   

Now, import CategoryContext in ChildB.js.

import React from 'react'  
import ChildC from './ChildC'  
import {CategoryContext} from '../App'  
  
function ChildB() {  
    return (  
        <div>  
            <ChildC/>  
            Child Component at level 2  
            <CategoryContext.Consumer>  
                {  
                    category => {  
                       return <div><b>Category is {category}</b></div>  
                    }  
                }  
            </CategoryContext.Consumer>  
              
        </div>  
    )  
}  
  
export default ChildB   

The output will be displayed as below.

Now, we will import the CategoryContext in ChildC.js to see how nested consumer works.

import React from 'react'  
import {UserContext,CategoryContext} from '../App'  
  
function ChildC() {  
    return (  
        <div>  
            Child Component at level 3  
            <UserContext.Consumer>  
                {  
                    user => {  
                        return(  
                            <CategoryContext.Consumer>  
                                {  
                                    category=>{  
                                        return <div><b>Context value is {user} and Category value is {category}</b></div>  
                                    }  
                                }  
                            </CategoryContext.Consumer>  
  
                        )   
                    }  
                }  
            </UserContext.Consumer>  
        </div>  
    )  
}  
  
export default ChildC   

Now, it will display the output as below.

As in the above example, we have seen how to consume the context, we need to approach multi-level nesting, but it is very difficult to read and understand so to make it more readable, here, we will make use of the useContext() hook.

Let’s see a demo of useContext() to use context value in a more readable and understandable format,

The process of passing creating context and passing value remain the same; we just need to make changes while consuming values.

Update code in ChildB.js to consume context value.

import React,{useContext} from 'react'  
import ChildC from './ChildC'  
import {UserContext,CategoryContext} from '../App'  
  
function ChildB() {  
    const user = useContext(UserContext)  
    const category = useContext(CategoryContext)  
    return (  
        <div>  
            <ChildC/>  
            Child Component at level 2  
            <div><i>User is {user}, Category is {category}</i></div>  
              
        </div>  
    )  
}  
  
export default ChildB   

As we can see in the above code, we just need to import useContext and after assigning the user.

Add Context and CategoryContext value to constant variables and we can simply use it as seen above.

The output is displayed in italics as below.

Using the useContext() hook makes code more readable and simpler to use.

4. useState() Hook

Let’s look at the demo in which we will see addition, multiplication, subtraction and reset a value available in Textbox.

Create a component MathHooks.js

import React, { useState } from 'react'      
function MathHooks() {      
    const initialVar = 1;      
    const [Var, setVar] = useState(initialVar)      
    return (      
        <div>      
            <h3> Value: {Var} <br /></h3>      
            <button onClick={() => setVar(Var + 2)}>Addition</button>      
            <button onClick={() => setVar(Var - 2)}>Subtraction</button>      
            <button onClick={() => setVar(Var * 2)}>Multiple</button>      
            <button onClick={() => setVar(initialVar)}>Reset</button>      
        </div>      
    )      
}      
export default MathHooks   

and import in App.js

import React from 'react';      
import './App.css';      
import MathHooks from './components/MathHooks'      
function App() {      
    return (      
        <div className="App">      
            <MathHooks></MathHooks>      
        </div>      
    );      
}      
export default App;   

This will display the output as below.

Now, click on the "Addition" button. It will add value by 2, here I clicked it 3 times.

Now, click on "Subtract" which will decrease the value by 2.

In the same way, click on the "Multiply" button which will multiply by 2.

Now, click on the "Reset" button. It will reinitialize the value.

The approach used in previous and this article for creating hooks for addition is unsafe; the reason and cause is displayed below.

Add another button which will include an addition by 5.

import React, { useState } from 'react'      
function MathHooks() {      
    const initialVar = 1;      
    const [Var, setVar] = useState(initialVar)      
    const AdditionBy5 = () => {      
        for (let i = 0; i < 5; i++) {      
            setVar(Var + 1)      
        }      
    }      
    return (      
        <div>      
            <h3> Value: {Var} <br /></h3>      
            <button onClick={() => setVar(Var + 2)}>Addition</button>      
            <button onClick={AdditionBy5}>Addition by 5</button>      
            <button onClick={() => setVar(Var - 2)}>Subtraction</button>      
            <button onClick={() => setVar(Var * 2)}>Multiply</button>      
            <button onClick={() => setVar(initialVar)}>Reset</button>      
        </div>      
    )      
}      
export default MathHooks  

Now look at the demo.

Click on addition by 5.

It is still increasing the Var value by 1 This is because setVar method is taking the old value of Var instead of the new value.

To resolve this issue, we need to implement the second form of useState() in which instead of passing direct value of variable, we will pass an arrow function which has access to old value, and it will take old value and increment the old value by 1 and assign the new value to the variable.

import React, { useState } from 'react'      
function MathHooks() {      
    const initialVar = 1;      
    const [Var, setVar] = useState(initialVar)      
    const AdditionBy5 = () => {      
        for (let i = 0; i < 5; i++) {      
            setVar(prevVar => prevVar + 1)      
        }      
    }      
    return (      
        <div>      
            <h3> Value: {Var} <br /></h3>      
            <button onClick={() => setVar(Var + 2)}>Addition</button>      
            <button onClick={AdditionBy5}>Addition by 5</button>      
            <button onClick={() => setVar(Var - 2)}>Subtraction</button>      
            <button onClick={() => setVar(Var * 2)}>Multiply</button>      
            <button onClick={() => setVar(initialVar)}>Reset</button>      
        </div>      
    )      
}      
export default MathHooks    

The output will be displayed as below.

So, in any case where you need to update state value dynamically you should go for a safer option, now let us change the code for other available buttons.

import React, { useState } from 'react'      
function MathHooks() {      
    const initialVar = 1;      
    const [Var, setVar] = useState(initialVar)      
    const AdditionBy5 = () => {      
        for (let i = 0; i < 5; i++) {      
            setVar(prevVar => prevVar + 1)      
        }      
    }      
    return (      
        <div>      
            <h3> Value: {Var} <br /></h3>      
            <button onClick={() => setVar(prevVar => prevVar + 2)}>Addition</button>               
            <button onClick={() => setVar(prevVar => prevVar - 2)}>Subtraction</button>      
            <button onClick={() => setVar(prevVar => prevVar * 2)}>Multiply</button>      
            <button onClick={() => setVar(initialVar)}>Reset</button>    
        </div>      
    )      
}    
export default MathHooks   

The output will remain the same as above.

Similarly, it should be implemented in the class component.

Update code in the previous article for MathClass.js.

import React, { Component } from 'react'      
class MathClass extends Component {      
    constructor(props) {      
        super(props)      
        this.state = {      
            addition: 0      
        }      
        this.incrementValue = this.incrementValue.bind(this)      
    }      
    incrementValue = () => {      
        this.setState(prevState => {      
            return {      
                addition: prevState.addition + 1      
            }      
        })      
    }      
    render() {      
        return (      
            <div>      
                <label>Added Value is {this.state.addition}</label><br></br>      
                <button onClick={this.incrementValue}>Add 1 Value</button>      
            </div>    
        )    
    }    
}    
export default MathClass    

The output will remain the same.

Now, let’s move to another example of useState().

useState() with objects

Now, in this case, instead of passing a single string or number in useState() function, objects will be passed. These objects can be anything like String, Numbers, Boolean, Object, Event or Array based on your requirement.

Let’s look at the demo to create full name along with prefix,

Create a component ObjectHooks.js

import React, { useState } from 'react'      
function ObjectHooks() {      
    const [name, setName] = useState({ prefix: '', firstName: '', lastName: '' })    
    return (      
        <div>      
            <form>      
                <input type="text" value={name.prefix} onChange={e => setName({ prefix: e.target.value })} />      
                <input type="text" value={name.firstName} onChange={e => setName({ firstName: e.target.value })} />      
                <input type="text" value={name.lastName} onChange={e => setName({ lastName: e.target.value })} />      
                <h2>Your full name is {name.prefix} {name.firstName} {name.lastName}</h2>      
            </form>      
        </div>      
    )      
}      
export default ObjectHooks  

import in App.js

import React from 'react';      
import './App.css';      
import ObjectHooks from './components/ObjectHooks'      
function App() {      
   return (      
      <div className="App">      
         <ObjectHooks></ObjectHooks>      
      </div>      
   );      
}      
export default App;    

This will display output as below.

You will notice that output is weird as when typing in the first textbox it is displaying the value, and as soon as it switches to nthe ext textbox it clears the value of previous ones,  and only take current one,  because useState will not merge the state, unlike setState in-class component.

So, to do that in functional component, we need to do it manually using the spread operator as shown in the code below,

import React, { useState } from 'react'      
function ObjectHooks() {      
    const [name, setName] = useState({ prefix: '', firstName: '', lastName: '' })    
    return (      
        <div>      
            <form>      
                <input type="text" value={name.prefix} onChange={e => setName({ ...name, prefix: e.target.value })} />      
                <input type="text" value={name.firstName} onChange={e => setName({ ...name, firstName: e.target.value })} />      
                <input type="text" value={name.lastName} onChange={e => setName({ ...name, lastName: e.target.value })} />      
                <h2>Your full name is {name.prefix} {name.firstName} {name.lastName}</h2>      
            </form>      
        </div>      
    )      
}      
export default ObjectHooks   

This spread operator dictates the method that creates a replica of the whole object and just updates the followed property,

Now check the output below, type the prefix for the name,

Add your first name,

Adding the last name,

Now state value is maintained and not lost.

useState with Array

In this scenario, we will learn about how to use useState when using Array,

Let’s look at the demo,

import React, { useState } from 'react'    
    
function ArrayHooks() {    
    const [lists, setLists] = useState([])    
    
    const addList = () => {    
        setLists([...lists, {    
            id: lists.length,    
            value: Math.floor(Math.random() * 5) + 1    
        }])    
    }    
    
    return (    
        <div>    
            <button onClick={addList}>Add List Item</button>    
            <ul>    
                {    
                    lists.map(list => (    
                        <li key={list.id}>{list.value}</li>    
                    ))    
                }    
            </ul>    
        </div>    
    )    
}      
export default ArrayHooks   

Import the file in App.js

import React from 'react';  
import './App.css';  
import ArrayHooks from './components/ArrayHooks'  
  
function App() {  
  return (  
    <div className="App">  
      <ArrayHooks></ArrayHooks>  
    </div>  
  );  
}  
  
export default App;  

This will display output as below,

Now click on the button,

It will add items to list and numbers are generated randomly.

Thank you for reading!

If you enjoyed this article, please share it with others who may enjoy it as well.!

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React Hooks Tutorial for Beginners: Getting Started With React Hooks

React hooks tutorial for beginners, learn React hooks step by step: Introduction, useState Hook, useState with previous state, useState with object, useState with array, useEffect Hook, useEffect after render, Conditionally run effects, Run effects only once, useEffect with cleanup, useEffect with incorrect dependency, Fetching data with useEffect, useContext Hook, useReducer Hook, useReducer, Multiple useReducers, useReducer with useContext, Fetching data with useReducer, useState vs useReducer, useCallback Hook, useMemo Hook, useRef Hook

React Hooks Tutorial - 1 - Introduction

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

React Hooks Tutorial - 2 - useState Hook React Hooks Tutorial - 3 - useState with previous state React Hooks Tutorial - 4 - useState with object React Hooks Tutorial - 5 - useState with array React Hooks Tutorial - 6 - useEffect Hook React Hooks Tutorial - 7 - useEffect after render React Hooks Tutorial - 8 - Conditionally run effects React Hooks Tutorial - 9 - Run effects only once React Hooks Tutorial - 10 - useEffect with cleanup React Hooks Tutorial - 11 - useEffect with incorrect dependency React Hooks Tutorial - 12 - Fetching data with useEffect Part 1 React Hooks Tutorial - 13 - Fetching data with useEffect Part 2 React Hooks Tutorial - 14 - Fetching data with useEffect Part 3 React Hooks Tutorial - 15 - useContext Hook Part 1 React Hooks Tutorial - 16 - useContext Hook Part 2 React Hooks Tutorial - 17 - useContext Hook Part 3 React Hooks Tutorial - 18 - useReducer Hook React Hooks Tutorial - 19 - useReducer (simple state & action) React Hooks Tutorial - 20 - useReducer (complex state & action) React Hooks Tutorial - 21 - Multiple useReducers React Hooks Tutorial - 22 - useReducer with useContext React Hooks Tutorial - 23 - Fetching data with useReducer Part 1 React Hooks Tutorial - 24 - Fetching data with useReducer Part 2 React Hooks Tutorial - 25 - useState vs useReducer React Hooks Tutorial - 26 - useCallback Hook React Hooks Tutorial - 27 - useMemo Hook React Hooks Tutorial - 28 - useRef Hook Part 1 React Hooks Tutorial - 29 - useRef Hook Part 2 React Hooks Tutorial - 30 - Custom Hooks React Hooks Tutorial - 31 - useDocumentTitle Custom Hook React Hooks Tutorial - 32 - useCounter Custom Hook React Hooks Tutorial - 33 - useInput Custom Hook

React Hooks Tutorial: Learn `useState` and `useEffect` in 5 minutes

React Hooks Tutorial: Learn `useState` and `useEffect` in 5 minutes

Sometimes 5 minutes is all you've got. So in this article, we're just going to touch on two of the most used hooks in React: `useState` and `useEffect`.

Sometimes 5 minutes is all you've got. So in this article, we're just going to touch on two of the most used hooks in React: useState and useEffect.

If you're not famliar with hooks, here's the TL;DR: because of hooks, there's almost no more need for class-based components. Hooks let you "hook into" the underlying lifecycle and state changes of a component within a functional component. More than that, they often also improve readability and organization of your components.

useState

Let's begin with a functional component.

import React from 'react';

function App() {
  return (
    <div>
      <h1>0</h1>
      <button>Change!</button>
    </div>
  );
}

As you can see, nothing fancy at the moment. We're just rendering some text and a (useless) button.

Now let's import our very first hook, useState to learn how to handle state in our functional component.

As this hook is a function, let's console.log what we get returned from it.

import React, { useState } from 'react';

function App() {
  const value = useState();
  console.log(value);

  return (
    <div>
      <h1>0</h1>
      <button>Change!</button>
    </div>
  );
}

In the console, we get an array

> [null, ƒ()]

And when we pass an argument to useState

const value = useState(true);

In the console, we get an array with our value as the first member.

> [true, ƒ()]

Now, in our component, we can access our state at value[0] and render it in <h1> instead of a hardcoded value.

import React, { useState } from 'react';

function App() {
  const value = useState(0);
  console.log(value); // [0, ƒ()]

  return (
    <div>
      <h1>{value[0]}</h1>
      <button>Change!</button>
    </div>
  );
}

We can improve our code by using array destructuring to store the value from useState hook. It's similar to object destructuring, which tends to be a bit more commonly seen. In case you're not super familiar with object destructuring, here's a quick recap:

const person = {
  name: 'Joe',
  age: 42
};

// creates 2 const values from person object
const { name, age } = person;
console.log(name); // 'Joe'
console.log(age); // 42

Array destructing is almost the same, but uses square brackets [] instead of curly braces {}.

A quick tip: in object destructuring, the names of created variables must match the names of properties in the object. For array destructuring, that's not the case. It's all about the order. The benefit here is we can name the items whatever we want.

Using array destructuring, we can get the initial value of state from the useState() hook.

import React, { useState } from 'react';

function App() {
  // remember, there's a second item from the array that's missing here, but we'll come right back to use it soon
  const [count] = useState(0);  

  return (
    <div>
      <h1>{count}</h1>
      <button>Change!</button>
    </div>
  );
}

OK, we've got the initial state value. How do we change the value in the state with hooks?

Remember that useState() hook returns an array with 2 members. The second member is a function that updates the state!

const [count, setCount] = useState(0);

You can, of course, call it what you wish, but by convention, it's normally called with prefix "set-", and then whatever state variable we wish to update was called, so setCount it is.

It's simple to use this function. Just call it and pass the new value you want that state to have! Or, just like this.setState in a class component, you can pass a function that receives the old state and returns the new state. Rule of thumb: do this anytime you need to rely on the past state to determine the new state.

To call it, we'll pass it to the onClick event listener. And just like with a regular setState in a class-based component, we can pass our state update to setCount.

function App() {
  const [count, setCount] = useState(0);

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={() => setCount(prevCount => prevCount + 1)}>
        Change!
      </button>
    </div>
  );
}

We can clean this up a bit, by extracting our state update to a separate function.

function App() {
  const [count, setCount] = useState(0);

  function change() {
    setCount(prevCount => prevCount + 1);
  }

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={change}>Change!</button>
    </div>
  );
}

Great! And now when we can see the counter going up when we click the button.

Of course, useState can get a lot more complicated than this, but we've only got 5 minutes here, so let's move on to the next hook for now.

useEffect

Hooks have simplified quite a few things, compared to the way things were in class-based components. Previously we needed to know a bit about lifecycle methods and which one is best suited for which situation. useEffect hook simplified this situation. If you wish to perform side effects, network request, manual DOM manipulation, event listeners or timeouts and intervals.

useEffect hook can be imported just like useState.

import React, { useState, useEffect } from 'react';

To make useEffect do something, we pass it an anonymous function as an argument. Whenever React re-renders this component, it will run the function we pass to useEffect.

useEffect(() => {
  /* any update can happen here */
});

This is what the whole code might look like.

import React, { useState, useEffect } from 'react';

function App() {
  const [count, setCount] = useState(0);

  function change() {
    setCount(prevCount => prevCount + 1);
  }

  useEffect(() => {
    /* any update can happen here */
  });

  return (
    <div>
      <h1>{count}</h1>
      <button onClick={change}>Change!</button>
    </div>
  );
}

export default App;

As an example, we will use a nice npm package that generates a random color. Feel free to write your own if you wish of course, but for this tutorial, we will just install it, npm i randomcolor, and import.

import randomcolor from 'randomcolor';

Let's now use our knowledge about useState hook to store some random color in the state.

const [color, setColor] = useState(''); // initial value can be an empty string

We then can then assign the color of the counter we already have.

<h1 style={{ color: color }}>{count}</h1>

Now, just for the sake of it, let's change the color of the counter on every click of the Change! button. useEffect will run every time the component re-renders, and the component will re-render every time the state is changed.

So if we write the following code, it would get us stuck in an infinite loop! This is a very common gotcha with useEffect

useEffect(() => {
  setColor(randomcolor());
});

setColor updates state, which re-renders the component, which calls useEffect, which runs setColor to update the state, which re-renders the component... Yikes!

We probably only want to run this useEffect when the count variable changes.

To tell useEffect which variable(s) to keep track of, we give an array of such variables as a second argument.

useEffect(() => {
  setColor(randomcolor());
}, [count]);

This basically says "only run this effect if the count state changes. This way we can change the color and not cause our effect to run infinitely.

Conclusion

There's a lot more to learn about hooks, but I hope you've enjoyed this quick 5-minute peek into hooks.

Happy coding 🤠

Hire React Js Developer from Expert React JS Development Company

Hire React Js Developer from Expert React JS Development Company

Are you looking to [hire React JS developer](https://www.mobiwebtech.com/react-js-development-company/ "hire React JS developer") from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team...

Are you looking to hire React JS developer from a prestigious and reliable React JS development company? Visit at Mobiweb Technologies here we have a big team of expert React JS developers, you can hire them on an hourly, weekly or monthly basis for the successful completion of your dream project.