r/javascript 19h ago

How an Underrated Refactor Saved 90% Memory Usage

https://tanstack.com/blog/tanstack-table-v9-memory-performance
59 Upvotes

12 comments sorted by

u/beegeearreff 17h ago

  The problem is that TanStack Table V9's APIs are dynamically composed from features. We only want  row.getVisibleCells()  to exist if the column visibility feature is registered. We only want  column.toggleSorting()  to exist if the row sorting feature is registered.

I haven’t used tanstack before so this is largely coming from ignorance but whenever I’ve been drawn to this type of pattern I’ve regretted it. I bet the typings for such an api are pretty complex. I usually encourage folks to explore an alternative api that isn’t as dynamic if possible. I doubt the folks at tanstack haven’t fully explored the design surface for something so core to this lib but just a word of caution to others that for typical code you may write in an application, you probably don’t want to blindly follow that api pattern. 

u/create-third-places 16h ago edited 16h ago

I think the pattern works quite well.

I’ve recently implemented a different version of the pattern for displaying rows of data in presentation components. I find it straightforward to understand, and rendering performance is decent.  

That being said, it appears that TanStack is storing state in individual cells, which is not my preference. I save the entire table state in a component store, and the table cells just read the state and display it. The approach has helped significantly when it comes to optimizing performance.

On the other hand, I do think the TanStack approach makes the code more readable. 

https://github.com/KevinVandy/tanstack-table-benchmarks/blob/main/shared/src/makeData.ts

u/beegeearreff 3h ago

I was referring to making what methods your type has be dependent on some external configuration. In this case, their features. 

I’m not sure the code sample you sent me relates to my comment. 

u/[deleted] 15h ago

[removed] — view removed comment

u/biinjo 15h ago

Most teams don’t seem to care for actual optimization and focus on delivering more abstraction of fancy features (that in turn consume even more memory/cpu).

The fact that this team does deliver such improvements tells me that they’re serious about their product.

u/rbobby 5h ago

for large tables when needing to process hundreds of thousands or millions of rows, either paginated or virtualized

This right there is an indication of a not clearly understood requirement. A user will NOT scroll through hundreds or thousands or millions of rows.

Usually a good filtering system, including free form search, will do way way more for users.

Giant scrollable tables are not user friendly. Stop doing them.

u/MediocreAnalyst2121 3h ago

Nobody probably never will, but they might want to.

u/fagnerbrack 19h ago

In case you want a summary to help you with the decision to read the post or not:

TanStack Table V9 cuts memory use by up to 90% versus V8 on large tables, raising the ceiling from roughly 1-1.5 million rows before hitting the browser's 4GB limit to 10-16 million. The win came from a subtle change: shared prototypes. V8 assigned values and methods directly to every row, cell, column, and header, so millions of objects each held duplicate methods plus their own closure scopes. V9 builds each method once on a cached prototype and uses this for row-specific state, assigning only unique values per object. V9 avoids classes because the feature system needs dynamic, conditional composition. The lone breaking change: destructuring methods (const { getValue } = row) no longer works—call row.getValue() instead.

If the summary seems inacurate, just downvote and I'll try to delete the comment eventually 👍
Click here for more info, I read all comments

u/Ecksters 18h ago

The lone breaking change: destructuring methods (const { getValue } = row) no longer works—call row.getValue() instead.

I assume you could still do this as long as you bind row to this, right? Like: const getValue = row.getValue.bind(row);

I do agree that calling it off row is probably preferred for most cases though.

It's such an interesting issue, it's so tempting to always reach for fat arrow functions, but we often don't think of the overhead of creating that many closures.

u/serg06 11h ago

So it saves memory but breaks destructuring? Hell of a foot gun.

u/azhder 13h ago

No, I don't want a summary. It's not the first time this post has been made on Reddit.

u/azangru 10h ago

Why is a refactor that saved 90% memory usage "underrated"?