Skip to content

feat: add support for dynamic row height #170

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 60 commits into from
Jun 22, 2020
Merged

Conversation

jamesonhill
Copy link
Contributor

@jamesonhill jamesonhill commented Jun 8, 2020

This adds a new prop estimatedRowHeight, which updates the Grid component to use VariableSizeGrid from react-window instead of FixedSizeGrid. When estimatedRowHeight is provided, rowHeight is ignored and content is dynamically sized. estimatedRowHeight is used as the default height while measuring the content.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 8, 2020

Thanks for your contribution, that's a great feature, I have some questions before I take some time for a detailed review

  • this package is supposed to support React@^16.0.0 while you are using some new api only available in 16.3.0, that would be a breaking change(although I don't think that would be a big problem)
  • Have you tested with frozen columns, in that mode the there would be 2 or 3 tables actually to achieve the frozen feature, so I think you need to check if the current height is the largest when setting the row height

@jamesonhill
Copy link
Contributor Author

@nihgwu Hi, yes it does currently work w/ frozen columns as well as frozen rows. LeftGrid, RightGrid, and MainGrid each call setRowHeight, https://github.com/Autodesk/react-base-table/pull/170/files#diff-1ce5af19cf8e0ddaa9ce889d8ac30d42R455, and if the key already exists and new size is bigger it updates the size. I am going to push one small update now so that it's not directly mutating the state object though.

@jamesonhill
Copy link
Contributor Author

@nihgwu As far as using createContext from React @16.3, I considered including that bump here but wasn't sure if it was appropriate or not. I can add it if you think so. It would be really fantastic if it could eventually get bumped to React@16.8 so that we can use hooks for new features.

src/BaseTable.js Outdated
@@ -315,7 +316,10 @@ class BaseTable extends React.PureComponent {
? dataGetter({ columns, column, columnIndex, rowData, rowIndex })
: getValue(rowData, dataKey);
const cellProps = { isScrolling, cellData, columns, column, columnIndex, rowData, rowIndex, container: this };
const cell = renderElement(cellRenderer || <TableCell className={this._prefixClass('row-cell-text')} />, cellProps);
const cellClasses = cn(this._prefixClass('row-cell-text'), {
[this._prefixClass('row-cell-text-dynamic')]: this.props.useDynamicRowHeight,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think the dynamic should be promoted to the row(row--dynamic) and use :not(.row--dynamic) { .cell-text{} }

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

perhaps we should promote even further to the top level like table--dynamic, as all the rows would have the same row--dynamic className

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was thinking maybe only applying row-cell-text when not in dynamic mode. None of those properties have any affect when content is dynamically sized, so always adding them and overriding is un-necessary work.

src/BaseTable.js Outdated
@@ -445,8 +449,22 @@ class BaseTable extends React.PureComponent {
);
}

setRowHeight = (index, size) => {
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we don't use arrow functions in a class component

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Have you considered about expansion? The indices would change on expand/collapse

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Expansion seems to have fine performance for me locally. I can update this to not use arrow functions.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think i see what you were alluding to w/ expansion. It seems its not entirely correct. I'll dig into fixing it.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think you can use rowKey as the key instead of index, as you can have totally different data with the same index

src/BaseTable.js Outdated

if (rowHeightMap.hasOwnProperty(index)) {
if (size > rowHeightMap[index]) {
rowHeightMap[index] = size;
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If you are using index as the key, why not use an array directly?
also you should use {...map, [index]: size} to mutate the ref
and if there is no need to re-render, you don't need to always setState at the bottom

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't use an array bc of how rowIndex is negative for frozen rows. It seemed simpler to pass props.rowIndex directly through instead of adding logic to figure out original index.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yes, I totally forget that 👍

@nihgwu
Copy link
Contributor

nihgwu commented Jun 8, 2020

Yes in the next major version I will use hooks, but for this version we should try to avoid breaking changes

@nihgwu
Copy link
Contributor

nihgwu commented Jun 8, 2020

For the context, I remember there is a compatible package there?

@jamesonhill
Copy link
Contributor Author

Perhaps. I haven't used any but I can check to see if one exists.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 9, 2020

I haven't check if we do need the new context, but here is the polyfill for it https://github.com/jamiebuilds/create-react-context

Copy link
Contributor

@nihgwu nihgwu left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Great job, thanks

src/BaseTable.js Outdated
@@ -1168,6 +1204,7 @@ BaseTable.propTypes = {
ExpandIcon: PropTypes.func,
SortIndicator: PropTypes.func,
}),
useDynamicRowHeight: PropTypes.bool,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

maybe we could use estimatedRowHeight for initial height and scrollToRow

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this value is presented, we use VariableSizeGrid and ignore rowHeight

src/BaseTable.js Outdated
@@ -473,14 +499,18 @@ class BaseTable extends React.PureComponent {
rowRenderer={this.renderRow}
onScroll={this._handleScroll}
onRowsRendered={this._handleRowsRendered}
useDynamicRowHeight={useDynamicRowHeight}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think we can use rowHeight: func|number here, then we don't need to pass so many extra props

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Do you think rowHeight should become optional then? If user provides number or func, we use that otherwise it's internally calculated using the TableRow.getBoundingClientRect().height that I added?

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think that's not the same case, for the BaseTable, the rowHeight could be optional as there is a default value, so actually we can remove the isRequired in the propTypes definition without breaking anything, and if we provide estimatedRowHeight we will ignore rowHeight and use estimatedRowHeight instead

for the GridTable, we use rowHeight: number as before, but rowHeight: func in dynamic mode, like rowHeight = index => rowHeightMap[index], in this way we don't need to change the props interface at all

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ok, I think i'm following. I'll push an update and see if we're on same page.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 9, 2020

For get to mention that if you have tested with the infinite loading feature, I think we can use the innerRef to get the totalRowHeight instead of calculate from the count and rowHeight https://github.com/Autodesk/react-base-table/blob/master/src/BaseTable.js#L150, in that way I think we can make that feature works

@nihgwu
Copy link
Contributor

nihgwu commented Jun 17, 2020

@jamesonhill I just pushed another commit, now you can reset cache without losing performance on resizing, it looks much better now
P.S. it's me who added shouldForceUpdate to react-window 😁

@jamesonhill
Copy link
Contributor Author

I haven't been able to track down what's causing it yet, but i'm seeing a little bit of "weird-ness" the first time I hover over a row (using dynamic heights). It gets smaller while hovering, then goes back to the correct size onMouseLeave and it doesn't happen on re-hover.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 20, 2020

@jamesonhill I've noticed that problem too, will inspect it

@nihgwu
Copy link
Contributor

nihgwu commented Jun 20, 2020

@jamesonhill I've tracked down the issue, but just don't know why ;(, when you hover on a row the first time, the this.props.style === prevProps.style = false for the mainTable but true for the frozen tables, then the row height in the main table won't be measured, but I just don't understand how it happens, after reset, the style should be from the cache in react-window and the compare check should be true

@jamesonhill
Copy link
Contributor Author

@nihgwu yea, that is odd. I'm debugging it and it even looks like they are the same unless i'm missing something.

@jamesonhill
Copy link
Contributor Author

Oh, nevermind. It looks like first render is using the estimatedRowHeight values, which makes sense.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 21, 2020

@jamesonhill the object value is the same but the reference is different before/after reset cache

@nihgwu
Copy link
Contributor

nihgwu commented Jun 21, 2020

@jamesonhill I've fixed the issue, and I think it's ready to be merged, please help to review the code I changed, thanks

@jamesonhill
Copy link
Contributor Author

@nihgwu it looks good to me. I've done some local testing and don't see the hover issue anymore. Scrolling w/ dynamic row heights is quite fast too! I think it's ready to merge.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 22, 2020

@jamesonhill Cool, let's land it, thank you so much 🎉

@nihgwu nihgwu merged commit 784b475 into Autodesk:master Jun 22, 2020
@nihgwu nihgwu deleted the 1.0.0-patch branch June 22, 2020 14:02
@jamesonhill
Copy link
Contributor Author

Awesome, thanks for your assistance & guidance. This should only require a minor version bump to the npm package?

@nihgwu
Copy link
Contributor

nihgwu commented Jun 22, 2020

sure, I'm experimenting with List instead of Grid, if it's a easy switch I want to include it, or I'll release a new version soon

@nihgwu
Copy link
Contributor

nihgwu commented Jun 22, 2020

v1.10.0 is released, I've make List work but it requires api change on onScroll, so let's do it in the next major version

@nihgwu nihgwu mentioned this pull request Jun 22, 2020
18 tasks
@nihgwu
Copy link
Contributor

nihgwu commented Jun 23, 2020

@jamesonhill I noticed an issue regarding the out sync of measurement in productionhttps://autodesk.github.io/react-base-table/examples/dynamic-row-heights, I guess it's caused by the fact that it's much faster in production that debounce of _updateRowHeights doesn't work well as expected, so it won't wait for the row in three tables be measured and updated in a single batch, if I enlarge the debounce delay, it works fine but there is noticeable lag, maybe I have use another mechanism instead of buffer

@jamesonhill
Copy link
Contributor Author

hmm, I'm actually seeing it locally but not in that production example.

@nihgwu
Copy link
Contributor

nihgwu commented Jun 23, 2020

LOL, that's interesting, I'm trying to fix it now

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants