When working with lists in React, the go-to optimization for many developers is useMemo:
const filtered = useMemo(() => tasks.filter(filter), [tasks, filter]);
const sorted = useMemo(() => filtered.sort(cmp), [filtered, cmp]);
This looks right, but it isn't:
- If you mutate the array in place, the reference doesn’t change and
useMemowill return stale results. - If you recreate the array every render (common in immutable flows), the memo dependency always changes and you pay the computation cost every time anyway.
Hence, I built a tiny alternative — memotable (≈1.4 KB min+gz). It’s a read-friendly data structure that maintains derived views efficiently. This matters only for a narrow set of apps (typically apps that manage large in-memory datasets).
Example
Instead of the typical write-friendly pattern-
const todos = new Map<string, ITodo>();
function getTodos(listId: string) {
return Array.from(todos.values())
.filter((todo) => todo.listId === listId
.sort(byTitle);
}
getTodos("list1"); // Full pass and sort on every invocation
you can express the same as a read-friendly structure using memotable like-
const todos = new Table<string, ITodo>();
todos.sort(byTitle);
todos.index(
(todo) => todo.listId,
(p) => p.memo(), // Memoize list level partitions
);
todos.partition("list1"); // Pre-computed list enabling fast read
todos.values(); // Root partition is not-memoized and hence still sorts at read-time
If your app works with thousands of items and reads are much more common than writes, memotable may be useful. As an example- based on my synthetic benchmark with R/W ratio of 4:1, memotable is ~4x faster.
If you’ve hit similar problems with list-level memoization — in React or otherwise — I would love to hear about how you approached it.
This is a tiny library by design, and I’m still refining where it adds the most value. Contributions, feedback or comments - all are welcome: https://github.com/shudv/memotable
Top comments (1)
Author here - happy to answer questions or hear any feedback!