feat: Add Stable/useStable and use for GridTableProps.rows/columns#933
feat: Add Stable/useStable and use for GridTableProps.rows/columns#933stephenh wants to merge 15 commits into
Conversation
| GridColumn, | ||
| GridDataRow, | ||
| GridTable, | ||
| GridTableUnsafe as GridTable, |
There was a problem hiding this comment.
@bdow I'd added GridTableUnsafe as an alias with the old/as-is props, but kinda wondering if maybe we should take this opportunity to introduce Table as the new component name, which has the new/safe props, and GridTable is the old/deprecated component name.
That would make this roll out a lot easier, b/c all exists clients of GridTable wouldn't have to be either a) renamed over to the GridTableUnsafe alias, or b) have their non-memoed rows/columns fixed.
...although arguably b) would be a great forcing function, it just means rolling this out will be a lot more work/updates.
Wdyt?
There was a problem hiding this comment.
Yea, I like that idea to rename the "new" to just Table. Then we won't block updating Beam on updating existing GridTable implementations.
There was a problem hiding this comment.
Cool! Pushed up the "GridTable stays the same, Table is the new safe one".
bdow
left a comment
There was a problem hiding this comment.
Nice! Would love to do a FE Guild discussion on this.
| // With this extension, we could almost get away without our own `useStable` hook, | ||
| // but we can't use the extension mechanism to restrict useMemo's `deps` to be | ||
| // `ReadonlyArray<StableDep>`. | ||
| function useMemo<T>(factory: () => T, deps: ReadonlyArray<unknown> | undefined): Stable<T>; |
There was a problem hiding this comment.
@bdow FWIW during demos, answering a question for @khalidwilliams , I'm kinda thinking we should back away from this change, which "upgrades" useMemo to produce stable values, and have useStable be the only one that can make stable values.
The reason is that, with the TS typing, I'm unable to "upgrade" useMemo's dependency list to be only stable values, and it is precisely this restriction that we'd need to have prevented the bug I fixed.
I.e. the bug I fixed, the const columns = useMemo(...) was "already useMemo-d" but the elevations dependency was itself an unstable array:
So, really, the fix we need to be 100% bulletproof to performance is not just: a) the user uses useStable or useMemo for the rows/columns props, but also b) any dependencies the user used for useStable or useMemo must themselves be stable.
And I cannot "fix" useMemo to have this restriction (it would also be a huge breaking change), so I think we'd have to just say "now all const rows = useMemo(...)s have to be const rows = useStable(...)s" and deal with the fallout.
I'm pretty convinced this is the right direction, even though it's a more disruptive change, b/c all of our useMemos lines need to become useStables--and also make sure their dependencies are stables, but that's the whole point, we need to have the stableness check be transitively applied to dependencies of our dependency.
Wdyt?
This PR attempts to solve the "damn it,
columnswas not memo'd so we blew up the table's performance and/or messed up the DOM focus" bugs by forcingcolumnsandrowsto really be stable.GridTableProps.rowsandcolumnswith theStable<...>marker/branded typeuseMemooruseStableuseMemo's return type to beStable<T>so existinguseMemo-d values will be considered stableuseStableis a new hook that is "justuseMemo" but requires its own dependency array to be stable, which is safer thanuseMemowhich to date just has a handful of eslint heuristics to check invalid depsGridTableUnsafethat is "the old non-stable API" to ease the transitionGridTableas-is, and introduce a newGridTableSafe, and move pages over to it one-by-one ... tempted to just pull the bandaid with the breaking change, but it will be a large roll out effort