Introduction
Memoization is a concept that's interesting and I believe all Javascript developer should be fascinated and familiar with it.
I will be going through this subject by in the following way: WHAT, WHY and HOW
1. What is Memoization?
When I first saw the word memoization what came to my mind was memorize, and I was confused! like how is JavaScript supposed to memorize and remember something on my behalf(I was thinking, is there some form of machine learning in JavaScript by default), but going deep into the concept of memoization I understood that it is all about helping JavaScript to memorize previous computation.
In-order words, memoization is a optimization technique that helps to speed-up repetitive, expensive computation by remembering the result of the previous computation.
2. Why Memoization?
The technique is revolved about making computation efficient and faster; If an expensive computation is done, with Memoization the result can be stored in eg. cache and retrieved when it's needed, hence there's no need to re-compute.
You have the all this with memoization, and in-addition you get efficient computation, optimization and faster computation (since it skips what has been done before).
3. How to implement Memoization?
In JavaScript?
Implementing memoization is about passing a function to a memoized callback
const multiplyBy10 = (num: number) => num * 10;
console.log('Simple call', multiplyBy10(3));
/**
*
* Explanation: a simple memoize function that takes in a function
*
* @param fn a function to be memoized or used to perform computation
* @returns a memoized function
*/
const memoize = (fn: Function) => {
let cache = {};
return (...args) => {
let num = args[0]; // just taking one argument here
if (num in cache) {
console.log('Fetching from cache');
return cache[num];
} else {
console.log('Calculating result');
let result = fn(num);
cache[num] = result;
return result;
}
}
}
// creating a memoized function for the 'multiplyBy10' pure function
const memoizedAdd = memoize(multiplyBy10);
console.log(memoizedAdd(3)); // calculated
console.log(memoizedAdd(3)); // cached
console.log(memoizedAdd(4)); // calculated
console.log(memoizedAdd(4)); // cached
thanks to Codegrepper & Agreeable Armadillo for the code reference
In React
There are several ways of implementing memoization and this is based on what is needed to be done
- for component use React.memo()
- If you want to memoize a function then use React.useCallback();
- If you want to memoize the result of an expensive function then use React.useMemo();
These methods prevent un-neccessary re-rendering in react if used the right way
Understanding React.memo()
/**
* Explanation:
* this function accept a name and render a styled version of it
*
* @param name
* @returns JSX.Element (styled version of the name)
**/
import React from 'react';
function RenderName({ name }: string) {
return <span className="text-red-500">{name}</span>
}
export default React.memo(RenderName);
Consider the component above(child-component) without the React.memo() higher order component (HOC) we'll have issues with re-rendering when the name passed to the RenderName component are the same; but React.memo() HOC helps to prevent this unnecessary re-renders
NB: memo does not optimize a function being passed to child component, that's why we need React.useCallback()
Understanding React.useCallback()
/**
* Explanation:
* a password field that handles users password
* and provides a toggle for the password
*
* @returns JSX.Element (a styled password field)
*/
import React from 'react';
import eye from 'images/eye.svg';
import eyeClose from 'images/eye-close.svg';
function PasswordInput() {
const [show, setShow] = React.useState<boolean>(false);
const toggleSecret = React.useCallback(() => setShow(x => !x), []);
return (
<div className="h-8 flex items-center">
<input type="password" className="..." placeholder="Enter Password" />
<button onClick={toggleSecret}>
<img src={show ? eyeClose : eye} alt="toggle secret" />
</button>
</div>
);
}
export default PasswordInput;
with React.useCallback() the toggleSecret() function is memoized and whenever you click this the toggle button it doesn't cause a re-render there by increasing optimization.
Note: we passed the arrow function to the React.useCallback(). the arrow function is:
() => setShow((x) => !x)
React.useCallback() also helps to prevent re-rendering when a function is being passed down to a child component, This is done by 'keeping' the function passed to the child component while the parent component re-render.
Understanding React.useMemo()
/**
* Explanation:
* The code demonstrates how to create a DynamicTable using React's useMemo() function.
* The DynamicTable component is a wrapper around the Table component.
* The DynamicTable component is responsible for creating the columns and data for the Table component.
*
* @param {values: Record<string, string>[] | null}
* @returns returns a JSX.Element(Table)
*/
import React, { useMemo } from 'react';
import Table, { ColumnsProps } from './Table';
interface DynamicTableType {
values: Record<string, string>[] | null;
}
const DynamicTable = ({ values }: DynamicTableType): JSX.Element => {
const columns: ColumnsProps[] = useMemo(() => {
if (!values) return [];
const keys = Object.keys(values?.[0]);
const result = [];
for (let index = 0; index < keys.length; index++) {
const element = keys[index];
result.push({
Header: element?.replace('_', ' '),
accessor: element,
});
}
return result;
}, [values]);
const data: Record<string, string>[] = useMemo(() => {
if (!values) return [];
const result: Record<string, string>[] = [];
for (let index = 0; index < values.length; index++) {
const element = values[index];
result.push(element);
}
return result;
}, [values]);
return <Table columns={columns} data={data} showSearch={false} />;
};
export default DynamicTable;
Extract from an open-source project I'm currently working on, check it out on github
The whole idea behind React.useMemo() is similar to that of React.useCallback(); but in React.useMemo() the result returned from the function calculation is being cached, so you dont have to repeat such expensive calculation (provided that your dependent array of the useMemo does not change). In the above example, columns & data are the memoized value.
Conclusion
In all, Optimization is something we should care about as Engineers and simple techniques like caching can help us prevent re-rendering/optimization issues etc. Memoization is only needed, when you are handling expensive calculation.
I am available for technical talks on data-structure, algorithms, javascript, react and react-native; you can reach out to me via twitter or email
I will also start a youtube channel soon to highlight my jouney with data-structure and algorithms, In the channel, I will be solving some leetcode questions and anyother interview materials I lay my hands on. watch out 😊
Footnotes
Thanks, for checking out this tutorial. (please like and add your comments)
You can also check out my other write-ups on my blog
If you have any questions, feedback or comments, please let me know.
You can connect with me on twitter email github
You can also reach out to me(I do React-Native & React Js) twitter email
Top comments (0)