react useMemo, useCallback. Easier than we think.
Tags: react, reactnative, javascript, frontend
Published: 2023-07-22 19:54:27

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))
          }
        >
          &laquo;
        </button>
        <button
          disabled={num === employees.length}
          onClick={() =>
            setNum((prevNum) =>
              prevNum < employees.length ? prevNum + 1 : prevNum
            )
          }
        >
          &raquo;
        </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.