One of the biggest "problems" with React is that it is such an out-of-your-way ecosystem that teams are able to build their own preferred abstractions whenever they want. In the mindset of many web developers, we're all geniuses who surely can invent the more optimal wheel - whether that's because we work at $BIG_CORP and have the time, or because we work in a start-up like environment with nobody to audit us and tell us we're collectively acting like dumb asses.
I've personally wasted several hundred hours this year implementing custom table logic and design through the react-virtualized-table/react-window libraries. They are great libraries! But it was not a good use of my time customizing it into a perfect incantation/API. I over-engineered how the virtualization tied into our REST API and many of our junior developers couldn't keep up with the increased abstraction.
Let my story be a lesson that you should probably grab hold of a library like this, swallow the fact that you're conforming to someone else's API and concepts, and just get stuff done.
I'll seriously evaluate and likely use this library in future react projects instead of other more involved implementations. Great docs too!
I too have gone down the same road as you. I wish I could share your sentiment of - just go with React Table.
But React Table is headless. Which is nice, but it's not going to automatically virtualize rendering. From what I can tell you are going to have to manually render using react virtualized/window or some other solution to get that to work. It also doesn't support things like fixed rows or columns out of the box since it doesn't provide UI.
My initial reaction is that this is a rather complicated API to group, sort, filter, expand, re-order, and select rows within data structures. It also uses a lot of prop spreading which is not my favorite.
I'm not sold that you wasted your time. But I'm going to give it a little more time as my initial assessment could be inaccurate.
I would love to hear how you would solve these problems yourself. Any ideas are very appreciated. Still, the patterns and tradeoffs built into React Table are not fleeting exploratory concepts that we hope will catch on, they are architected this way for a purpose. Sure, it doesn't come with any opinionated UI, but in the future, opinionated UIs for React Table will likely exist (some are already in development by 3rd parties). Until then, there are plenty of examples that show you just how easy it is to implement things like virtualization. Having control of the markup for a data grid can be intense and intimidating for those who aren't use to building their own tables, but again, once you see how little work there is to do in your own components, you'll see the power in the library (like having full control over styles, building your own UI APIs, prop patterns, state management paradigms, etc.) As for the prop spreading............... It's fantastic and I would love to see anyone convince me of a more elegant/composable pattern for decorating arbitrary and unknown markup.
Fair take, but I also believe we should've thrown out virtualization/windowing entirely. It was of those shiny product-level features that sounds great but ultimately provides little to no value for most applications.
Most of my point is engineer-oriented but I think it also applies at the product level. React allows you to build cool features easily - that doesn't mean every cool idea should be acted upon.
At times this will mean "dumbing-down" design in the name of pragmatism > shininess. YMMV, ad-supported businesses are going to need that infinite scroll... but most don't.
Agreed. Most if not all of the production tables I have are using very simple architecture. Pagination + Data Exploration utilities like sort/filter/group/etc.
In no way am I preaching that these plugins should used. However, I am proud that they can be used. And if you end up being one of the users that only needs a few things to make your production tables great, then you win again by being able to treeshake out all of the other stuff you choose not to use.
I went through quite some trouble to get a react-table v6 based datagrid updated to v7.
I found that the examples showed individual features well, but when you combine features you are on your own. The "Kitchen Sink" example is far from having all features enabled at once.
The reason why I couldn't stay with v6 was that the virtualization examples used a deprecated API from an old minor version. I would have had to support a solution on a non-supported version (v6 has been pretty much abandoned).
So, I had to get these features to coexist peacefully:
- expanded rows
- virtualization
- sorting
- custom cell rendering
- click actions inside the cells (open a dialog, add the row entity to comparison)
Mirroring the GP, I had to use react-window and deal with the complexity from the library. I think that virtualization should be a first-class citizen inside react-table, like sorting.
On top of all of this the TypeScript support/typings are tricky to work with (IIRC you have to maintain a .d.ts file in your codebase and remember to update it when you update react-table). I literally had a headache for hours on while going through the refactoring.
In the end I managed to get everything to work without any bugs or weird behavior. I really was enthusiastic about v7, but was left with a bad feel of it.
I'm sure that things will get better, and luckily most folks don't beed all of these features. It's really the virtualization that makes things complicated. The thing is, pagination is not always a viable solution.
Some of React’s data tables are not that great for anything other than the most conventional cases. You are mostly right, but I don’t think making your own table is equatable to like some of the dumbass things people inject into their ecosystem for a simple app.
/signed - the dumbass that builds his own table components.
Why was the virtualization tied into to your Rest API? If I give you an array of N things, you render them. If you need more records, ask me, and I'll get them for you. The table should be just be a UI component with virtualization. It shouldn't need to know how to retrieve its data.
- Lightweight (5kb - 12kb+ depending on features used and tree-shaking)
- Headless (100% customizable, Bring-your-own-UI)
- Auto out of the box, fully controllable API
- Sorting (Multi and Stable)
- Filters
- Pivoting & Aggregation
- Row Selection
- Row Expansion
- Column Ordering
- Animatable
- Virtualizable
- Resizable
- Server-side/controlled data/state
- Extensible via hook-based plugin system
Just to speak up for those that are struggling with the headlessness of v7... I have a small team with junior resources that had to build a table in React.
We were hoping for a plug-and-play library, and eventually settled on React Table v6. Being a small team, I don’t think we’ll migrate over to v7 - we don’t have any benefits from any more control over the UI than v6 had.
Unrelated but I'm really not a fan of how managers like to dehumanize when they speak about what are really their colleagues -> You 'have' a team of 'resources'.
Calling them 'reports' is also bad but a least it doesn't sound like something is going to be used up
It's completely independent of your data sources. You provide the data and tell it how to set up filtering and sorting and such, it provides the transformed rows, you render the rows as desired.
A plugin is currently in development to make this extremely easy and turn-key, but for now (since you control the rendering layer), you can do this on your own with a few lines of code in your table renderer.
There's probably a way to get at the lot via a string, but by index was quick and easy enough.
One thing I do have to complain about is the lack of native TypeScript support - there used to be types in the repo, but they got removed. And `prepareRow(row)` is a bit ugly, but nothing major and it's only a one-time call.
Yeah, I'm sorry that's the case now. Having the types as first-class citizens of the repo while the library was not written in TS or maintained by TS devs was a big mistake. They are still available (and will updated asap from some other great devs) in DT. The prepareRow function may be ugly, but not as ugly as a freakishly slow table with only a handful of rows and columns visible at once ;)
The first thing I looked for was screenshots in the readme. But one has to go look for it many clicks deep and launch a 3rd party editor (which can be buggy), and let whatever engine parse and run the code to render the whole thing, just to see what it looks like.
You have a huge colorful banner at the top of the readme, why not screenshots?
Being a headless table utility, any screenshot of the table UI would either be over or under selling the potential UI that users will implement with the library. If you noticed the exmamples are very bare bones styles so as to highlight the functionality of the library. These examples would be "ugly" as marketing material. If another example was super dressed up and looked amazing and used as marketing material, users may be disappointed when they realize they are in charge of their own styles. It's 6's I believe, so I went with what I thought was the safest option, exclusion of screenshots.
I suppose then, a screenshot of source code next to the rendered output is what programmers desire, in the introduction. Instead of thinking "That's a sexy table!", the programmers will hopefully think "That's some sexy table-generating code!".
While this is a good piece of work I was surprised that it seemed to be impossible it to control table search and filter from outside of components. I blame hooks as their behavior seems to be non-trivial to push outside of component boundaries. Which puts developer in undesirable constraints how to model application.
You can control the table from wherever you can put the `useTable` hook. 99% of the time this will be in your table component, but nothing is stopping you from hoisting this higher or decorating it with whatever other logic you want. Even if you chose not to hoist the hook, there is also nothing stopping you from listening to props and using callbacks to update this state. This is not a constraint of the API and more a constraint of how you choose to model your component-hook composition points.
Tell me if I'm wrong, but skimming the docs I understand that the table's filter state is in the local state of the table (state defined in the hook).
If it's the case, it does mean that the table component isn't controlled (as in "controlled input component"). What you describe (if I understand correctly) is _synchronising_ the local state of the table with my own global state, instead of controlling the table (giving my global state as param of the hook). This is breaking the one-way data flow that makes React so good.
But I might be misunderstanding something, in which case please correct me! I am very interested in your library, the pattern has been around for a while but tables in React are always a PITA as they combine the problems of forms + lists + crazy interactivity
It's one-way control. There is no reasonable way to implement narrative "now my filter resides in global state. Whenever component changes, it's reflected in the state. Whenever state changes it's reflected in component". I asked this question in spectrum community https://spectrum.chat/react-table/general/how-to-control-fil...
There's definitely a difference between taking table state and storing it somewhere globally for use later and 100% controlling the table state from outside of the table. Doing the latter involves listening to the table state for changes in an effect, shipping the new state up to your higher-than-component storage location, detecting in that same component a change in the global storage location (usually just a memoized prop or hook of sorts) and updating the table state with your updated global state using either a table method like instance.setFilter or even a state reducer if you want total control. The tools are in place and the API is flexible enough to do what you are referring to. I'm sorry your comment in Spectrum got lost in the noise and that you had to resort to posting your question on here.
Thanks for detailed explanation and your time. I do understand that things I want to achieve are achievable. But I'm not happy with the complexity price I have to pay.
I believe that moving in the implementation between local and global state should be a snap. It's a matter of time when need to move local state to global will arise. And hooks have nothing to propose for that.
This would be acceptableway to go. But I did not manage to get it working. Saw your comment how to fix, but don't have code to validate that everything would work as expected.
I've personally wasted several hundred hours this year implementing custom table logic and design through the react-virtualized-table/react-window libraries. They are great libraries! But it was not a good use of my time customizing it into a perfect incantation/API. I over-engineered how the virtualization tied into our REST API and many of our junior developers couldn't keep up with the increased abstraction.
Let my story be a lesson that you should probably grab hold of a library like this, swallow the fact that you're conforming to someone else's API and concepts, and just get stuff done.
I'll seriously evaluate and likely use this library in future react projects instead of other more involved implementations. Great docs too!