React introduced the useMemo and useCallback hooks in React 16.8, which was released on February 6, 2019. So, as of August 2024, these hooks are five years and 6 months old.
Here is the GitHub repo with the entire code. Below, you will find the link to the example page.
Example page hosted on Vercel -> https://nextjs14-kafka-tracking.vercel.app/
useMemo (calculateValue, dependencies)
const cachedValue = useMemo(calculateValue, dependencies)Definition useMemo
The useMemo hook in React is used to optimize performance by memoizing the result of a function so that it only recomputes when one of its dependencies changes. This is particularly useful for expensive computations that otherwise run on every render.
calculateValue is a function intended for computation within the component.
dependencies is an array containing values.
When the component renders initially, useMemo is invoked. Subsequently, whenever the value(s) in the dependencies change, calculateValue runs. If the values in the dependencies remain unchanged, the cached data is utilized, preventing unnecessary renders.
In Strict Mode, React calls the calculation function twice to maintain component purity, ensuring React components yield the same output for the same input. This behavior is exclusive to development mode and does not affect the logic.
Example Code “useMemo”
const calculateValue = (num) => {
// Some expensive calculation
return num * 2;
};
function MyComponent({ number }) {
const memoizedValue = useMemo(() => calculateValue(dependencies), [dependencies]);
return <div>{memoizedValue}</div>;
}In this example, useMemo will only recompute the result of calculateValue if dependencies changes, otherwise it returns the previously cached result.
Replacement or Alternatives in React 18
React 18 doesn’t provide a direct replacement for useMemo, but it introduces features like automatic batching and concurrent rendering that can improve performance in other ways. However, useMemo is still relevant and widely used for optimizing specific scenarios where recalculating values on every render would be costly.
The new features in React 18 can help in reducing the need for optimizations like useMemo in some cases, but there's no direct alternative or replacement. useMemo continues to be a useful tool in managing performance.
Real World Example “useMemo”
Below, you will find the link to a previous post in which I used the useMemo Hooks in a real-world example to create a custom Dashboard.
Next.js 14 -Advanced Analytics with Tinybird and integrated Dashboard
I used the useMemo hooks in all five Dashboard Components to save the data result. In the example below from the function useTrend. If the [data] “dependency” changes the useMemo Hook will run again.
"use client";
import { BarChart } from "@tremor/react";
import Widget from "../Widget";
import useTrend from "@/lib/hooks/use-trend";
import { useMemo } from "react";
import moment from "moment";
export default function TrendWidget() {
const { data, status, warning } = useTrend();
const chartData = useMemo(
() =>
(data?.data ?? []).map((d) => ({
Date: moment(d.t).format("HH:mm"),
"Number of visits": d.visits,
})),
[data]
);
return (
<Widget>
<div className="flex items-center justify-between">
<Widget.Title>Users in last 30 minutes</Widget.Title>
<h3 className="text-neutral-64 font-normal text-xl">
{data?.totalVisits ?? 0}
</h3>
</div>
<Widget.Content
status={status}
loaderSize={40}
noData={!chartData?.length}
warning={warning?.message}
>
<BarChart
data={chartData}
index="Date"
categories={["Number of visits"]}
colors={["blue"]}
className="h-32"
showXAxis={false}
showYAxis={false}
showLegend={false}
showGridLines={false}
/>
</Widget.Content>
</Widget>
);
}useCallback
const cachedFn = useCallback(fn, dependencies)Definition useCallback
Similar to useMemo, you can also use this hook to optimize performance. The useCallback hook memoizes a callback function and returns it.
Note that the useCallback hook memoizes the function itself, not its return value. useMemo Caches the function return value so that the function does not execute again. useCallback Caches the function definition or the function reference.
The useCallback hook in React is used to memoize functions, ensuring that a function is only recreated when its dependencies change. This is particularly useful when passing functions as props to child components, as it prevents unnecessary re-renders of those child components by ensuring that the function reference remains the same unless necessary.
Example Code “useCallback”
function MyComponent({ onClick }) {
return <button onClick={onClick}>Click me</button>;
}
function ParentComponent() {
const [count, setCount] = useState(0);
const handleClick = useCallback(() => {
setCount((prevCount) => prevCount + 1);
}, []);
return <MyComponent onClick={handleClick} />;
}In this example, useCallback memoizes the handleClick function. The function will only be recreated if the dependencies array (which is empty in this case) changes. This helps in avoiding unnecessary re-renders of MyComponent if the ParentComponent re-renders for reasons unrelated to the handleClick function.
Real World Example “useCallback”
In the code below, I use the “useCallback” Hook to set the “setDateFilter” and it will be invoked on every change of the from / to Date.
"use client";
import moment from "moment";
import { useRouter, useSearchParams } from "next/navigation";
import { useCallback, useEffect, useMemo, useState } from "react";
import { DateRangePickerValue } from "@tremor/react";
import { DateFilter, dateFormat } from "@/types/date-filter";
export default function useDateFilter() {
const router = useRouter();
const searchParams = useSearchParams();
const params = new URLSearchParams(searchParams.toString());
const [dateRangePickerValue, setDateRangePickerValue] =
useState<DateRangePickerValue>();
const setDateFilter = useCallback(
({ from, to, selectValue }: DateRangePickerValue) => {
const lastDays = selectValue ?? DateFilter.Custom;
const startDate = from;
const endDate = to;
params.set("last_days", lastDays);
if (lastDays === DateFilter.Custom && startDate && endDate) {
params.set("start_date", moment(startDate).format(dateFormat));
params.set("end_date", moment(endDate).format(dateFormat));
} else {
params.delete("start_date");
params.delete("end_date");
}
router.push("/dashboard?" + params.toString(), { scroll: false });
},
// eslint-disable-next-line react-hooks/exhaustive-deps
[]
);
const lastDaysParam = searchParams?.get("last_days") as DateFilter;
const lastDays: DateFilter =
typeof lastDaysParam === "string" &&
Object.values(DateFilter).includes(lastDaysParam)
? lastDaysParam
: DateFilter.Last7Days;
const { startDate, endDate } = useMemo(() => {
const today = moment().utc();
if (lastDays === DateFilter.Custom) {
const startDateParam = searchParams?.get("start_date") as string;
const endDateParam = searchParams?.get("end_date") as string;
const startDate =
startDateParam ||
moment(today)
.subtract(+DateFilter.Last7Days, "days")
.format(dateFormat);
const endDate = endDateParam || moment(today).format(dateFormat);
return { startDate, endDate };
}
const startDate = moment(today)
.subtract(+lastDays, "days")
.format(dateFormat);
const endDate =
lastDays === DateFilter.Yesterday
? moment(today)
.subtract(+DateFilter.Yesterday, "days")
.format(dateFormat)
: moment(today).format(dateFormat);
return { startDate, endDate };
}, [
lastDays,
searchParams,
// searchParams?.get("start_date"),
// searchParams?.get("end_date"),
]);
useEffect(() => {
const vallastDays = lastDays === DateFilter.Custom ? "" : lastDays;
setDateRangePickerValue({
from: moment(startDate).toDate(),
to: moment(endDate).toDate(),
selectValue: vallastDays,
});
}, [startDate, endDate, lastDays]);
const onDateRangePickerValueChange = useCallback(
({ from, to, selectValue }: DateRangePickerValue) => {
if (startDate && endDate) {
setDateFilter({ from, to, selectValue });
} else {
setDateRangePickerValue({ from, to, selectValue });
}
},
[setDateFilter, startDate, endDate]
);
return {
startDate,
endDate,
dateRangePickerValue,
onDateRangePickerValueChange,
};
}The hooks “useMemo” and “useCallback” can greatly speed up our web project. Caching is key for a high conversation rate and a good positioning on the Google SERP’s.
Cloudapp-dev, and before you leave us
Thank you for reading until the end. Before you go:



