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

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.

This is image title

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.

This is image title

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

This is image title

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.

This is image title

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

This is image title

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.

This is image title

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

This is image title

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

This is image title

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?

This is image title

3. useContext() Hook

What is React Context

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

This is image title

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.

This is image title

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.

This is image title

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.

This is image title

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.

This is image title

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.

This is image title

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

This is image title

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

This is image title

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

This is image title

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

This is image title

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.

This is image title

Click on addition by 5.

This is image title

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.

This is image title

This is image title

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.

This is image title

This is image title

This is image title

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.

This is image title

This is image title

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.

This is image title

This is image title

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,

This is image title

Add your first name,

This is image title

Adding the last name,

This is image title

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,

This is image title

Now click on the button,

This is image title

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-js #javascript #react-hook #tutorial

React Hook: Using Memo() - Callback() - Context() - State()
1 Likes55.05 GEEK