Skip to content

TableView reduce renders and onLoadMore#3529

Closed
snowystinger wants to merge 7 commits into
mainfrom
fix-table-renders
Closed

TableView reduce renders and onLoadMore#3529
snowystinger wants to merge 7 commits into
mainfrom
fix-table-renders

Conversation

@snowystinger
Copy link
Copy Markdown
Member

@snowystinger snowystinger commented Sep 16, 2022

Closes

In collaboration with @devongovett
This addresses some extra renders and extra layout effect calls that were noticed in the resizing PR

useTableColumnResizeState now has internal state, where, if no prop "onResize", is provided, it'll use its own state to cause a re-render. However, if onResize is provided, then it's assumed that the component calling the hook will trigger a re-render in some fashion. This could be from a layout or their own state management.

We make use of the onResize in our TableView because we have a layout and virtualizer, so we invoke the re-render with a relayoutNow.

Previously useTableColumnResizeState called a state update, and then in an effect later, relayoutNow was called. Now we just go directly to the relayoutNow, skipping the first render that the setState triggered.

This brought up a case where we still had two renders though, as setTableWidth causes a relayoutNow and setVisibleRect causes a relayout. The order we had them in meant that relayoutNow was called and then another relayout was scheduled after a raf. Instead, we can coalesce those into one render by scheduling the relayout and then immediately doing the relayoutNow which will handle everything in the scheduled update immediately as well.

This brought up yet another issue where we had some tests that accounted for way too many calls to onLoadMore, which we've always felt was off anyways. Our number of renders was now correct, but our layout effect was being called with a stale value, resulting in onLoadMore being called when it shouldn't be. We considered a few different ways of running the layout effect, however, we found our common methods of choosing a dependency array to be insufficient. I've outlined what we considered in a comment in the code.

Performance testing

Initial Load:
no difference, the build on main spent 459ms in initial TableView, the build on my branch spent 450ms by comparison

Resize:
build on main each resize took on average 95ms
build on my branch each resize took on average 48ms

in the same span of about 1second, main managed 9 frames vs my branch did 14 frames
main also averaged 2-3 recalculate style calls to the browser in between each resize, while my branch averaged 1

one interesting thing, that i don’t quite know how it fits in is that main ran each resize under onGlobalMessage and barely ran anything under onPointerMove
meanwhile, my branch ran most of everything under onPointerMove and had a much much smaller onGlobalMessage call

✅ Pull Request Checklist:

  • Included link to corresponding React Spectrum GitHub Issue.
  • Added/updated unit tests and storybook for this change (for new code or code which already has tests).
  • Filled out test instructions.
  • Updated documentation (if it already exists for this component).
  • Looked at the Accessibility Practices for this feature - Aria Practices

📝 Test Instructions:

🧢 Your Project:

@adobe-bot
Copy link
Copy Markdown

Base automatically changed from col-resize-hit-area-increase to main September 19, 2022 22:32
@adobe-bot
Copy link
Copy Markdown

@adobe-bot
Copy link
Copy Markdown

Copy link
Copy Markdown
Member

@LFDanLu LFDanLu left a comment

Choose a reason for hiding this comment

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

Tested in Chrome, FF, Safari, and Chrome android, LGTM. Just one request for a comment update

Comment on lines +476 to +480
// choosing
// - state.contentSize.height > 0 && state.contentSize.height // if height reduces (delete the page) then we want to load more
// - state.virtualizer.contentSize.height // not an entry in dependency array, so if we remove the state.contentSize, this won't work if we removed the dependency we aren't using
// - ref for initial render
// - ref to store content size from virtualizer and remove dependency array <-
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

nit: this comment makes sense with the PR description's context, but I feel like it doesn't without that context. Mind just mentioning in this comment that these were potential entries in a dep array that didn't work out hence why we run it every render instead?

LFDanLu
LFDanLu previously approved these changes Sep 21, 2022
@adobe-bot
Copy link
Copy Markdown

@adobe-bot
Copy link
Copy Markdown

},
"dependencies": {
"@babel/runtime": "^7.6.2",
"@react-aria/utils": "^3.13.3",
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

This is an aria dependency in a stately package

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

never again!

Copy link
Copy Markdown
Member Author

@snowystinger snowystinger Sep 23, 2022

Choose a reason for hiding this comment

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

ugh.... fails SSR Test, i'll look into it more tomorrow

@adobe-bot
Copy link
Copy Markdown

@adobe-bot
Copy link
Copy Markdown

@adobe-bot
Copy link
Copy Markdown

Comment thread packages/@react-stately/table/src/useTableColumnResizeState.ts
@adobe-bot
Copy link
Copy Markdown

act(() => {jest.runAllTimers();});

expect(onLoadMore).toHaveBeenCalledTimes(3);
expect(onLoadMore).toHaveBeenCalledTimes(1);
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

I could see how the first one is not scrolling far enough to cause a onLoadMore to call, but shouldn't the second one? I'd want to see expect(onLoadMore).toHaveBeenCalledTimes(0); after the first two scrolls. Should there be a test after this that does call onLoadMore twice?

@snowystinger
Copy link
Copy Markdown
Member Author

closing in favor of #3672

@LFDanLu LFDanLu deleted the fix-table-renders branch January 11, 2023 01:34
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.

5 participants