As front-end developers, we always try to make our application performance better and better. Sometimes we need to execute large and critical calculation in our system. Those function/calculation take time to complete, so our application will be slow.
Meanwhile, we try to improve performance using the Memoization technique.
In this lesson, we will learn about Memoization technique using useMemo and useCallback.
Memoization:
Memoization or memoisation is an optimization technique used primarily to speed up computer programs by storing the results of expensive function calls to pure functions and returning the cached result when the same inputs occur again.
useMemo Hook
The useMemo hook is designed to memoize expensive computations. Memoization simply means caching. It caches the computation result with respect to the dependency values so that when the same values are passed, useMemo will just spit out the already computed value without recomputing it again. This can significantly improve performance when done correctly.
Syntax:
useMemo hook can be used as follows,
const memoizedResult = useMemo(() => {
return criticalComputeFn(parameter1, parameter2);
}, [parameter1, parameter2]);
Here, criticalComputeFn represents the calculation function and dependencies represent the dependency array. ([parameter1, parameter2]).
UseCallBack
useCallback does the same thing as useMemo but it returns a memoized callback instead of memoized value.
Syntax:
This hook can be used as follows,
const memoizedResult = useCallback(() => {
return criticalComputeFn(parameter1, parameter2);
}, [parameter1, parameter2]);
Remember one thing, useMemo and useCallback are both used for same purpose, but useMemo return memoized value but useCallback return memoized callback.
What is callback?
It is a function that is passed as an argument to another function.
Rules:
During the first rendering or initial rendering useMemo and useCallback both invoke criticalComputeFn, then useMemo returns the result/value but useCallback returns the callback.
If dependencies change then useMemo and useCallback both invoke criticalComputeFn, then useMemo return the result/value but useCallback return the callback.
If dependencies don't change, then both don't invoke criticalComputeFn but useMemo return memoize result/value and useCallback return memoize callback.
Simple Example:
import React, { useCallback, useMemo, useEffect } from 'react'
export default function App() {
const a = 5
const b = 7
const memoResult = useMemo(() => a + b, [a, b])
const callbackResultFn = useCallback(() => a + b, [a, b])
useEffect(() => {
}, [])
const callback = callbackResultFn()
console.log(callbackResultFn) // Return function
console.log(callback) // return value
console.log(memoResult) // return value using useMemo
return (
<div>
<><h2>Test test</h2></>
</div>
)
}
in the above code we can see that useMemo and useCallback show the same result/value. useMemo directly return the calculation result/value but useCallback return callback function then we need to process the result to show it.
Careful about:
useMemo/useCallback should be used only when it is necessary to optimize the computation. In other words, when recomputation is expensive.
It is advisable to first write the calculation without memoization and only memoize it if it is causing performance issues.
Unnecessary and irrelevant use of the useMemo/useCallback hook may even compound the performance issues.
Sometimes, too much memoization can also cause performance issues.
RealTime example of useMemo:
import axios from "axios";
import { useEffect, useMemo, useState } from "react";
export default function App() {
const [employee, setEmployee] = useState({});
const [employees, setEmployees] = useState([]);
const [num, setNum] = useState(1);
const endPoint =
"https://my-json-server.typicode.com/ifeanyidike/jsondata/employees";
useEffect(() => {
const getEmployee = async () => {
const { data } = await axios.get(`${endPoint}/${num}`);
setEmployee(data);
};
getEmployee();
return () => {};
}, [num]);
useEffect(() => {
axios.get(endPoint).then(({ data }) => setEmployees(data));
}, [num]);
const taxVariablesCompute = useMemo(() => {
const { income, noOfChildren, noOfDependentRelatives } = employee;
const reliefAllowance1 = 0.01 * income >= 200000 ? 0.01 * income : 200000;
const reliefAllowance2 = 0.2 * income;
const numChildren = +noOfChildren <= 4 ? +noOfChildren : 4;
const numRelatives =
+noOfDependentRelatives <= 2 ? +noOfDependentRelatives : 2;
const childrenRelief = numChildren * 2500;
const relativesRelief = numRelatives * 2000;
const pensionRelief = 0.075 * income;
const reliefs =
reliefAllowance1 +
reliefAllowance2 +
childrenRelief +
relativesRelief +
pensionRelief;
return reliefs;
}, [employee]);
const taxCalculation = useMemo(() => {
const { income } = employee;
let taxableIncome = income - taxVariablesCompute;
let PAYE = 0;
const taxEndPoints = [300000, 300000, 500000, 500000, 1600000, 3200000];
for (let i = 0; i < taxEndPoints.length; i++) {
if (i === 0) {
if (taxableIncome >= 300000) {
PAYE += 0.07 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.07 * taxableIncome;
break;
}
} else if (i === 1) {
if (taxableIncome >= 300000) {
PAYE += 0.11 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.11 * taxableIncome;
break;
}
} else if (i === 2 && taxableIncome >= 500000) {
if (taxableIncome >= 500000) {
PAYE += 0.15 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.15 * taxableIncome;
break;
}
} else if (i === 3) {
if (taxableIncome >= 500000) {
PAYE += 0.19 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.19 * taxableIncome;
break;
}
} else if (i === 4) {
if (taxableIncome >= 1600000) {
PAYE += 0.21 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.21 * taxableIncome;
break;
}
} else if (i === 5) {
if (taxableIncome >= 3200000) {
PAYE += 0.24 * taxEndPoints[i];
taxableIncome -= taxEndPoints[i];
} else {
PAYE += 0.24 * taxableIncome;
break;
}
}
}
const netIncome = income - PAYE;
return { PAYE, netIncome };
}, [employee, taxVariablesCompute]);
return (
<div className="App">
<div>
<p></p>
<p>Name: {`${employee.firstName} ${employee.lastName}`}</p>
<p>Job: {employee.job}</p>
<p>Sex: {employee.sex}</p>
<p>Income: {employee.income}</p>
<p>No. of children: {employee.noOfChildren}</p>
<p>No. of dependents: {employee.noOfDependentRelatives}</p>
<p>PAYE: ₦{taxCalculation.PAYE}</p>
<p>Income after PAYE: ₦{taxCalculation.netIncome}</p>
</div>
<div>
<button
disabled={num === 1}
onClick={() =>
setNum((prevNum) => (prevNum >= 1 ? prevNum - 1 : prevNum))
}
>
«
</button>
<button
disabled={num === employees.length}
onClick={() =>
setNum((prevNum) =>
prevNum < employees.length ? prevNum + 1 : prevNum
)
}
>
»
</button>
</div>
</div>
);
}
In the above code, I have tried to calculate tax information. I think it's an expensive calculation. So I do it with useMemo.
That's it for today. See yaa.
Top comments (0)