In this article, we analyse few arrow functions found in open source projects that are passed in as a parameter to useMemo or useCallback. I found these below list of functions in the wild, there could be more use cases but let’s control our sample size for this analysis.
useMemo and the arrow function
Let’s first understand what a useMemo is. useMemo is a React Hook that lets you cache the result of a calculation between re-renders.
const cachedValue = useMemo(calculateValue, dependencies)
Read more about useMemo. React docs provides some great examples such as
useMemo in Documenso
Documenso is as an open-source Docusign alternative. I have to admit, Documenso is good!!! I like its user interface and the best part is this is open-source and built using Next.js. You can read their source code to learn best practices such as uploading files, rendering pdf with quality in Next.js and so much more. I have added this repository to my collection to study in depth and hopefully produce some articles like this one in the future.
At line 48 in upload-document.tsx, you will find the below code snippet.
const disabledMessage = useMemo(() => {
if (remaining.documents === 0) {
return team
? msg`Document upload disabled due to unpaid invoices`
: msg`You have reached your document limit.`;
}
if (!session?.user.emailVerified) {
return msg`Verify your email to upload documents.`;
}
// eslint-disable-next-line react-hooks/exhaustive-deps
}, [remaining.documents, session?.user.emailVerified, team]);
In this case, results are cached based on these below values between re-renders:
remaining.documents
session?.user.emailVerified
team
useMemo in Tisqleditor
tisqleditor is a codeMirror6 based SQL code editor which is used in TiDB Cloud Console. I am not sure what a TiDB cloud console is but we are interested in useMemo with arrow function as its parameter.
const activeFile = useMemo(
() => openedFiles.find((f) => f.id === activeFileId),
[activeFileId, openedFiles]
)
In the above image, you will find that activeFile is assigned the value returned by useMemo call that has two dependencies. activeFileId, openedFiles. This means activeFile results are cached between re-renders.
React docs says that —
“
Just as {} creates a different object, function declarations like function() {} and expressions like () => {} produce a different function on every re-render. By itself, creating a new function is not a problem. This is not something to avoid! However, if the Form component is memoized, presumably you want to skip re-rendering it when no props have changed. A prop that is always different would defeat the point of memoization.
“
At this point, it is also worth mentioning useCallback.
useCallback
This below explanation is picked from React documentation.
To memoize a function with useMemo, your calculation function would have to return another function:
export default function Page({ productId, referrer }) {
const handleSubmit = useMemo(() => {
return (orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
};
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
This looks clunky! Memoizing functions is common enough that React has a built-in Hook specifically for that. Wrap your functions into useCallback instead of useMemo to avoid having to write an extra nested function:
export default function Page({ productId, referrer }) {
const handleSubmit = useCallback((orderDetails) => {
post('/product/' + productId + '/buy', {
referrer,
orderDetails
});
}, [productId, referrer]);
return <Form onSubmit={handleSubmit} />;
}
The two examples above are completely equivalent. The only benefit to useCallback is that it lets you avoid writing an extra nested function inside. It doesn’t do anything else. Read more about useCallback.
useCallback usage in Postiz
Postiz is an open-source social media scheduling tool.
At line 147 in messages.tsx, you will find the below code snippet.
const Page: FC<{ page: number; group: string; refChange: any }> = (props) => {
const { page, group, refChange } = props;
const fetch = useFetch();
const { message } = useContext(MarketplaceProvider);
const visible = usePageVisibility(page);
const loadMessages = useCallback(async () => {
return await (await fetch(`/messages/${group}/${page}`)).json();
}, []);
loadMessages contains a cached result except it does not contain any dependencies. Interesting. The component in which this function is defined has the following signature:
const Page: FC<{ page: number; group: string; refChange: any }> = (props) => {
About me:
Hey, my name is Ramu Narasinga. I study large open-source projects and create content about their codebase architecture and best practices, sharing it through articles, videos.
I am open to work on an interesting project. Send me an email at ramu.narasinga@gmail.com
My Github - https://github.com/ramu-narasinga
My website - https://ramunarasinga.com
My Youtube channel - https://www.youtube.com/@thinkthroo
Learning platform - https://thinkthroo.com
Codebase Architecture - https://app.thinkthroo.com/architecture
Best practices - https://app.thinkthroo.com/best-practices
Production-grade projects - https://app.thinkthroo.com/production-grade-projects
Top comments (0)