When implementing footnotes on this blog via React Server Components, I came across an interesting pattern that involves abusing the React cache function to mutate an object's properties.
For desktop screens, I like inline footnotes that show up in a sidebar. I implemented them via a simple component. Here's a rudimentary example11In reality, my footnote code is a tad more complex than this example with the mobile code removed:
If you edit a post to insert an earlier footnote you have to go through the whole post and increment any that follow. That's frustrating. I wondered if it could be tracked automatically and attempted a few solutions. The best solve I found was mutating an object's keys inside of React's cache function.
So let's update our component to use React's cache function-which has a lifetime tied to the current request-to track of how many footnotes we have. We can then mutate state across a request's lifecycle, spanning the React rendering tree.
Footnote.tsx
1import React, { cache } from 'react';
2
3export interface Props {
4 content: React.ReactNode;
5}
6
7// object references are stable in javascript, but we can mutate its keys!
8const indexCount = cache(() => ({ index: 0 }));
9
10export const footnoteContent = cache(() => [] as (Props & { index: number })[]);
Now we can render Footnote without the index and it dynamically generates the index for us:
1 <Footnote content="First footnote" />
2 <Footnote content="Second footnote" />
3 <Footnote content="Third footnote" />
We no longer have to keep track of what each footnote's index is - the cache function returns an object that is stable and we increment its index key in the render function.
Also, we can push our footnote content into an array22Arrays are objects in javascript, to render a FootnoteFooter later if we choose to.
FootnotesFooter.tsx
1import { footnoteContent } from './Footnote'; // and access it later in the react tree!
The FootnoteFooter here is only visible to mobile users. If you're on desktop, make your browser smaller and you can see it.
Not only do we no longer have to keep track of which index each footnote is, but we can wrap up all of our footnotes up at the end of each post by using a second cache to track the footnote content.
Let me know if you use this pattern in other ways!
1. In reality, my footnote code is a tad more complex than this example