-
Notifications
You must be signed in to change notification settings - Fork 2.7k
MM-36696: global threads list now virtualized #8350
Conversation
Global threads list now is virtualized to avoid rendering all thread items at once. This reduces the time needed to enter the threads route.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
non-blocking comments
const OVERSCAN_COUNT_BACKWARD = 4; | ||
const OVERSCAN_COUNT_FORWARD = 4; | ||
|
||
const virtListStyles = { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
do we need to have the style inline?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
DynamicSizeList
accepts a style
prop and not a class one.
My guess would be that since there are no stylesheets included in the module, there are only inline styles,
we pass inline styles so we can properly override if needed(?).
selectedThreadId={selectedThreadId} | ||
/> | ||
{currentFilter === ThreadFilter.unread && !someUnread && isEmpty(unreadIds) ? ( | ||
<NoResultsIndicator |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
shouldn't this be either the threadlist or the noresults? why nesting the no result indicator inside a list that won't be populated?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@Willyfrog it's the noresults for the unreads tab. So it's not like nesting in the list, but nesting in the left pane.
Would renaming the component from thread_list
to thread_list_pane
make it clearer?
PS:
We have 3 "noresults" cases:
- whole global threads page (when not following any thread)
- right pane (when there is no selected thread)
- left pane (threads list) unreads tab (when we have no unread threads) <--- the case we are discussing
// so we need to reverse the ids before feeding the list | ||
// so that we get the correct order afterwards. | ||
// | ||
// TODO: have dynamic-virtualized-list to maybe accept a direction prop |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nitpick: this should be an issue in the repo instead of a todo here
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Note that the repo was created originally by Sudheer as a fork of react-window, and since he left, I don't think anyone else has been maintaining it.
Edit: Also, that's funny that it works that way since I'm pretty sure the reason it does that is because we send the posts to the client sorted backwards (newest to oldest) which honestly has caused a lot of confusion in the post storage code in the past. I wonder if we should just store posts oldest to newest and then update the list component to never reverse the list.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Edit: Also, that's funny that it works that way since I'm pretty sure the reason it does that is because we send the posts to the client sorted backwards (newest to oldest) which honestly has caused a lot of confusion in the post storage code in the past. I wonder if we should just store posts oldest to newest and then update the list component to never reverse the list.
@hmhealey, it seems that it doesn't work :( I cannot find a way to add items at the top...
items added at the bottom (0 index) are getting rendered, but items added at the top (last index) don't...
I'd like to add back react-window
for this case.
@koox00 this seems great to me, but I'm not sure the test server is the best place to test on since it doesn't have a lot of data yet. Is there anything specific from a UX perspective that I should pay close attention to in the review? I'm not seeing anything glaring |
@matthewbirtch @koox00 do we want to add a loading indicator at the bottom of the list if you scroll down and threads are still fetching? I throttled my connection to "slow 3G" then scrolled down really fast and there was no indication that more threads were loading in until they were returned. In the center channel we have the three dots pulsing animation |
This can be important for slow or unstable connections ^ |
@esethna the "infinite" loading will be added later on, this PR is just to improve rendering performance of the list so that the time to enter threads view is reduced. |
Ah got it, thanks @koox00 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
One non-blocking comment, but otherwise this looks good to me.
components/threading/global_threads/thread_list/virtualized_thread_list.tsx
Outdated
Show resolved
Hide resolved
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Mostly refactors the use of selectors to avoid re-rendering much. - Selectors that are not memoized (via let's say createSelector) should pase shallowEqual to useSelector hook - Selectors which take an object as an argument should memo that object so that they don't re-render on every re-render, since the custom object would be created each time regardless if the property at hand hadn't changed Other changes unrelated to performance: - spans should not contain divs ideally - ThreadItem now accepts a style prop to be feeded by Virtualized list - ThreadViewer had shouldComponentUpdate so it can't extend PureComponent in the same time PS: react-dev-tools' profiler showed that a lot of re-renders are happening due to "hooks changed" so I tried to tackle them, but it's not specific on which hooks changed, so it could be useContext which redux uses or the react ones or our custon ones.
@hmhealey, @calebroseland I've added some changes to improve performance, can you take a look? |
This commit replaces DynamicSizeList with react-window's FixedSizeList. The reason is mostly dynamic-virtualized-list doesn't support adding lists at the top. The most notable change is that all thread items now have the same height.
/update-branch |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not seeing anything glaring from my tests. Seems to work/look great.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
LGTM!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Thanks @koox00
Tested, looks good to merge.
- Verified opening long global threads list, list renders as expected.
Test server destroyed |
@koox00 I didn't merge this due to conflicts, can you please take a look? Thanks. |
…st-webapp into MM-36696_crt-performance
/cherry-pick cloud |
Cherry pick is scheduled. |
* MM-36696: global threads virtualized list Global threads list now is virtualized to avoid rendering all thread items at once. This reduces the time needed to enter the threads route. * Fixes tests * Minor fixes * Fixes performance issues in global threads Mostly refactors the use of selectors to avoid re-rendering much. - Selectors that are not memoized (via let's say createSelector) should pase shallowEqual to useSelector hook - Selectors which take an object as an argument should memo that object so that they don't re-render on every re-render, since the custom object would be created each time regardless if the property at hand hadn't changed Other changes unrelated to performance: - spans should not contain divs ideally - ThreadItem now accepts a style prop to be feeded by Virtualized list - ThreadViewer had shouldComponentUpdate so it can't extend PureComponent in the same time PS: react-dev-tools' profiler showed that a lot of re-renders are happening due to "hooks changed" so I tried to tackle them, but it's not specific on which hooks changed, so it could be useContext which redux uses or the react ones or our custon ones. * Uses react-window for virtualized thread list This commit replaces DynamicSizeList with react-window's FixedSizeList. The reason is mostly dynamic-virtualized-list doesn't support adding lists at the top. The most notable change is that all thread items now have the same height. * Revert needless "optimization" Co-authored-by: Mattermod <mattermod@users.noreply.github.com> (cherry picked from commit cafeacf)
* MM-36696: global threads virtualized list Global threads list now is virtualized to avoid rendering all thread items at once. This reduces the time needed to enter the threads route. * Fixes tests * Minor fixes * Fixes performance issues in global threads Mostly refactors the use of selectors to avoid re-rendering much. - Selectors that are not memoized (via let's say createSelector) should pase shallowEqual to useSelector hook - Selectors which take an object as an argument should memo that object so that they don't re-render on every re-render, since the custom object would be created each time regardless if the property at hand hadn't changed Other changes unrelated to performance: - spans should not contain divs ideally - ThreadItem now accepts a style prop to be feeded by Virtualized list - ThreadViewer had shouldComponentUpdate so it can't extend PureComponent in the same time PS: react-dev-tools' profiler showed that a lot of re-renders are happening due to "hooks changed" so I tried to tackle them, but it's not specific on which hooks changed, so it could be useContext which redux uses or the react ones or our custon ones. * Uses react-window for virtualized thread list This commit replaces DynamicSizeList with react-window's FixedSizeList. The reason is mostly dynamic-virtualized-list doesn't support adding lists at the top. The most notable change is that all thread items now have the same height. * Revert needless "optimization" Co-authored-by: Mattermod <mattermod@users.noreply.github.com> (cherry picked from commit cafeacf) Co-authored-by: Kyriakos Z <3829551+koox00@users.noreply.github.com>
Summary
Global threads list is virtualized to avoid rendering all thread
items at once.
This reduces the time needed to enter the threads route.
Ticket Link
https://mattermost.atlassian.net/browse/MM-36696
Release Note