useMemo usage in Documenso, an open-source Docusign alternative.
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 us:
At Thinkthroo, we study large open source projects and provide architectural guides. We have developed reusable Components, built with tailwind, that you can use in your project.
We offer Next.js, React and Node development services.
Book a meeting with us to discuss your project.