what is difference between useEffect , useMemo and useCallback hooks in React?
what is difference between useEffect, useMemo and useCallback hooks in React?
it is a very big confusion when you first learn state management hooks to manage and observe the state in react, they seem to do the same job (to observe the state and alter it only when something changes that they depend on).
but there are some differences that we should know when we want to use them so that we dont use them in a wrong and inapproperiate way.
when its about managing state in react , there are three different hooks that come in, useEffect, useMemo and useCallback , so lets take a look at each of them and see the differences.
1 - useEffect :
useEffect hook is used to syncronize your state with an external system. something that is not controlled by react. like connecting with an API , event handling , third-party codebases …etc.
when a part of your code is relying on some external system
like an Api, an event or a third-party codebase, then you should use the useEffect hook to connect and syncronize your app with them.
in a simple language :
we use useEffect hook anytime we want to do something when anything changes in our app.
useEffect takes two arguments:
1 – setup (your effect logic)
2 – an array of dependecies
keep in mide that useEffect does not return any value.
if you return something insdde this hook that will be a clean up which we will talk about it later.
useEffect hook will run its code anytime that its dependencies change, for example:
if you skip the second parameter (array of dependencies) then this will couse an infinite loop so remember that you must proveide the second parameter for this hook. if you do not have anything that your setup code depends on , then provide an empty array as a second parameter and in this case your effect code will be excecuted only when your component mounts.
useEffect hook does not return a value for us to use instead it just performs our setup code when something chnages on its dependencies
but what if we return something in this hook?
the return is made in this hook for cleaning up the previous effects from the last time that this hook run, for example
if we use addEventListener method inside our effect logic , useEffect is responsible to listen for that particular event and
syncronise our app with that event. now if we no longer need that event in our app (e.g we render another component inside our app), we can get rid of that eventListener by returning another logic that removes that eventListener in this case:
import { useEffect , useState } from"react";
exportdefaultfunctionScreenWidth(){
const [width , setWidth] = useState(window.innerWidth);
useEffect(()=>{
window.addEventListener('resize', ()=>setWidth(window.innerWidth))
}
},[])
return(
<>
<h3>Screen Width : {width}</h3>
</>
)
}
in this example we calculate the screen width by adding an event listener of resize to the window object and display the window width anytime it changes so we will see the changes live.
we use useEffect hook because we are using an event and we want this calculation to be executed every time the screen is
resized by user and we will see the live preview of screen size changes.
now by returning a function inside useEffect, everytime that our useEffect hook is executed , first it runs the clean up logic
(removes the previous event) and then executes the setup code of useEffect (listens the new one).
import { useEffect , useState } from"react";
exportdefaultfunctionScreenWidth(){
const [width , setWidth] = useState(window.innerWidth);
useEffect(()=>{
window.addEventListener('resize', ()=>setWidth(window.innerWidth))
//if we return in useEffect so it should be a clean up function
return ()=>{
window.removeEventListener('resize',()=>setWidth(window.innerWidth));
}
},[])
return(
<>
<h3>Screen Width : {width}</h3>
</>
)
}
2 - useCallback:
useCallback hook is another hook for managing the state in react that is used to memoize (cache) a function.
useCallback also takes two arguments :
1 – a function
2 – an array of dependency
this is quite useful when we want to cache an entire function depending to the variables or values that it depends on.
the useCallback will always return the function itself, not the calculated value from that function.
for example if you have a function that you pass it to another component as a prop so everytime that your component rerenders that function will be created again and again and if you have a heavy operation inside your function , it will couse your component to be slow in performance so you can cache your function in the memory so that if the dependencies have’nt changed since the last render , there is no need to create a brand new function over and over again so your component will pass the cached function to receiver component until the dependencies changes.
let’s see an example to clearify this:
first we have two states , one for toggling the theme to dark or white , and another state for holding the value that comes from an input of type number in the page.
then we have a function named heavyFunction which as an example of a heavy operation it first counts from zero to one billion and then it multiplies the input number with 2 and returns it.
in the other hand we have a dark state in our page which is false by default and we toggle our page’s theme based on this dark state also we have another component named ShowMul which we pass our heavyFunction in this component as a prop and then it will call this function and display the result inside of itself.
this is our component:
import { useCallback , useEffect, useState } from "react"
export default function CalWeight(){
const [dark , setDark] = useState(true);
const [num , setNum] = useState(0)
const style ={
backgroundColor : dark? '#555' : '#eee',
color : dark ? '#fff' : '#000',
height : '400px'
}
function heavyFunction(){
for(let x =0 ; x<1000000000;x++){}
return Number(num) * 2;
}
function changeTheme(){
setDark(prev => !prev);
}
return(
<div style={style}>
<button onClick={changeTheme}>change theme</button>
<input type="number" value={num} onChange={(e)=>setNum(e.target.value)} />
<ShowMul heavyFunction ={heavyFunction}/>
</div>
)
}
function ShowMul({heavy}){
const [mul , setMul] = useState(1);
useEffect(()=>{
setMul(heavyFunction())
},[heavyFunction])
return(
<div>
<h3>Your number * 2 = {mul}</h3>
</div>
)
}
now everytime that the input number changes, the entire component rerenders, so it calculates from (0 – 1000000000) and then returns the multiplication of input number with 2 , since it is a heavy operation you can see almost 1 second delay when you change the number.
now anytime that this component rerenders the entire process starts over, even if you don’t touch the input number. it will start counting from zero to one billion and return the same value for you. for example now if you click the button to change the theme; it will run the heavy Function and then change the theme with delay , this is the situation that useCallback shines, so we don’t really need to run that function when we want to only change the theme , because we haven’t changed the number so there is no need to run that heavy operation.
so we can wrap that function inside useCallback hook and the problem will be solved.
import { useCallback , useEffect, useState } from "react"
export default function CalWeight(){
const [dark , setDark] = useState(true);
const [num , setNum] = useState(0)
const style ={
backgroundColor : dark? '#555' : '#eee',
color : dark ? '#fff' : '#000',
height : '400px'
}
const heavyFunction = useCallback(()=>{
for(let x =0 ; x<1000000000;x++){}
return Number(num) * 2;
},[num]);
function changeTheme(){
setDark(prev => !prev);
}
return(
<div style={style}>
<button onClick={changeTheme}>change theme</button>
<input type="number" value={num} onChange={(e)=>setNum(e.target.value)} />
<ShowMul heavyFunction ={heavyFunction}/>
</div>
)
}
function ShowMul({heavy}){
const [mul , setMul] = useState(1);
useEffect(()=>{
setMul(heavyFunction())
},[heavyFunction])
return(
<div>
<h3>Your number * 2 = {mul}</h3>
</div>
)
}
now useCallback will run the heavyFunction only when we really change the input number, otherwise it caches the function in the memory and if there is nothing different with number from the last render; it does’nt run that function, so we won’t see the delay if we change the theme.
since the useCallback will return the function itself , now we can pass it to other components , in this case we have passed it as a prop to ShowMul component and we can call it insdie ShowMul and we can do whatever we want with this function and this is the only thing that makes useCallback to stand out among other similar hooks in react.
3 - useMemo:
useMemo hook is another hook which is used to manage the state in react and as it is clear from its name , it is used to memoize or cache something in the memory so that it does’nt have to calculate it again and again when the component rerenders.
the noly difference between useMemo and useCallback is their retrun value, useMemo will return the calculated value of the function that uses this hook while useCallback returns the function itself that uses this hook.
useMemo also takes two arguments :
1 – a function
2 – an array of dependency
for example lets apply the same logic that we used on useCallback , and see the difference:
import { useState , useMemo } from "react";
export default function Use_memo(){
const [dark , setDark ] = useState(false)
const [number , setNumber] = useState(0);
const double = useMemo(()=> solo(number),[number]);
function solo(num){
console.log('calling the solo function')
for (let x = 0 ; x <= 1000000000 ; x++){}
return num *2;
}
const styles= {
backgroundColor : dark ? '#222' : '#eee',
color: dark ?'#fff' : '#000',
height : '300px'
}
return (
<>
<input value={number} onChange={e => setNumber(e.target.value)} type='number'></input>
<button onClick={()=>setDark(prev => !prev)} >change theme</button>
<div style={styles}>
<h2>{double}</h2>
</div>
</>
)
}
in conclusion:
you will use useEffect anytime that you are dealing with something that is not controlled by react.(api,event…)
you should use useCallback when you have a function that is also used by other components and you want to memoize (cache) the function (itself) so that it is not being created again and again as a brand new function in your components unless it is needed.
you should use useMemo if you want to memoize the returned value of a function so the process of calculating is’nt repeating when the component rerenders.