bvaughn / react-window

React components for efficiently rendering large lists and tabular data
https://react-window.now.sh/
MIT License
15.75k stars 783 forks source link

Support just-in-time measured content #6

Closed bvaughn closed 1 month ago

bvaughn commented 6 years ago

In order to avoid adding cost to existing list and grid components, create a new variant (e.g. DynamicSizeList and DynamicSizeGrid). This variant should automatically measure its content during the commit phase.

MVP

The initial implementation of this could work similarly to how CellMeasurer works in react-virtualized:

  1. Content is measured only if no current measurements exists.
  2. Measurements need to be reset externally (imperatively) if something changes.
  3. Cells at a given index can only be positioned after all cells before that index have been measured.

Goal

This component could perform better if we removed the third constraint above, allowing random access (by either item index or scroll offset) without measuring the preceding items. This would make react-window much more performant for use cases like chat applications.

This would also unlock the ability to use a ResizeObserver (via react-measure) to automatically detect item sizing and remove the position and measurements cache entirely. This would remove the need for imperatively resetting cached measurements and dramatically improve the API.

In order for the above to be possible, the dynamic list/grid components would need to use a dramatically different approach for mapping offset to index and vice versa. (This comment about "scroll anchoring" in react-virtualized has some nice visuals.) Essentially, we would need to do something like this:

screen shot 2018-06-10 at 11 58 38 am screen shot 2018-06-10 at 12 01 01 pm

The above approach has only one major downside: aligning items correctly at list boundaries. If item indices are estimated (as described above) then they likely won't line up exactly with the beginning or end of the scrollable area.

The one case that would still not be handled correctly with the above approach would be a scroll anchor that is set outside of the "safe zone" but a current scroll that goes inside of the safe zone (as shown below). If the user scrolls slowly back toward the beginning of the list, it may be difficult to align the first cell with zero without introducing scroll janky.

screen shot 2018-06-10 at 5 08 26 pm
thessem commented 4 years ago

@afreix @tastyqbit I am fairly certain your issues on Safari and Internet Explorer are due to the lack of support for ResizeObserver in those browsers. I had exactly your issue (scrolling back fixed it) and adding a polyfill solved it for me. See MDN for compatibility tables.

I think the lack of ResizeObserver should generate a warning in the console.

piecyk commented 4 years ago

What about something like this https://codesandbox.io/s/agitated-jennings-o9nn6 ?

basic using VariableSizeList + ItemMeasurer from #102 I know this is very naive approach but looks like it's kinda working ( not that bad 😂) as temporary solution 🤔

gnir-work commented 4 years ago

In our team we came across the need to render a large list of random sized textual data. For us the ability to accurately jump between items and scroll without any lags was a must. After quite a bit of online searching I gave up and wrote this library. Hope it helps as a temporary solution for some of you :smile_cat:

gnir-work commented 4 years ago

Hey, Just wanted to update that after some work I think that the library came to a decent state and could be a good solution if your items don't change their size after being rendered for the first time.

burtonator commented 4 years ago

@gnir-work what if the window resizes and the container size changes causing the list to resize?

burtonator commented 4 years ago

Reading this thread and I think there might be a better way to handle items that have a width that can change.

The definition of 'variable' here is variable over the whole set. The actual items size is static, not dynamic. Maybe dynamic is a better term because the size can change during resize or mutation if the text changes.

The way this could work is to listen to the the scrolling on the parent.

The algorithm would work like this:

The only downside to this is that the scrollbar height will change slightly. I'm not sure if this would be distracting to the user. Might be if the scroll list is large. We MIGHT be able to trick this by increasing/decreasing a buffer such that the estimated sizes don't impact the scroll bar length.

gnir-work commented 4 years ago

@gnir-work what if the window resizes and the container size changes causing the list to resize?

As of the latest version (2.2.0) the component supports changes to the size of the the whole list (height and width).

gnir-work commented 4 years ago

Reading this thread and I think there might be a better way to handle items that have a width that can change.

The definition of 'variable' here is variable over the whole set. The actual items size is static, not dynamic. Maybe dynamic is a better term because the size can change during resize or mutation if the text changes.

The way this could work is to listen to the the scrolling on the parent.

The algorithm would work like this:

  • compute an estimated size for each item - this needs to be very efficient. It doesn't have to be perfect. +- 20% is fine.
  • we only render items that are visible + say 20% more so that if the user scrolls they don't see any changes
  • We have a 'ghost' for everything else which has 'height' set to the estimated height. It's just an empty div. This way the scroll bar looks about right.
  • An item is only shown when it is in the viewport. Other than that the ghost items is shown.

The only downside to this is that the scrollbar height will change slightly. I'm not sure if this would be distracting to the user. Might be if the scroll list is large. We MIGHT be able to trick this by increasing/decreasing a buffer such that the estimated sizes don't impact the scroll bar length.

This was already implement by @bvaughn in react-virtualized. The main downside of this approach is that jumping to a specific row doesn't work well and creates visuals bugs when scrolling up.

burtonator commented 4 years ago

@gnir-work ah.. ok... maybe I'll abort my work and look at react-virtualized.

I think you're right with the scroll up issues but I think I can fix this by having an 'estimated size', and then convert it to an actual size once the component mounts.

... but another issue of course is handling dynamic content. If something updates the component height changes.

gnir-work commented 4 years ago

If the estimated size is accurate than it may work, For example I implement sometime ago a virtualized list with dynamic height with react-virtualized and it worked well as long as the estimated size was exact. Otherwise the scroll to row functionality would break my app ☹

Sent from Mail for Windows 10

From: Kevin Burton Sent: Tuesday, 14 April 2020 23:07 To: bvaughn/react-window Cc: Nir Geller; Mention Subject: Re: [bvaughn/react-window] Support just-in-time measured content (#6)

@gnir-work ah.. ok... maybe I'll abort my work and look at react-virtualized. I think you're right with the scroll up issues but I think I can fix this by having an 'estimated size', and then convert it to an actual size once the component mounts. ... but another issue of course is handling dynamic content. If something updates the component height changes. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

-- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

burtonator commented 4 years ago

@gnir-work ah... I don't need the 'scroll to row' functionality I think.. though maybe I'm wrong.

gnir-work commented 4 years ago

That this should suffice for your needs 😊

Sent from Mail for Windows 10

From: Kevin Burton Sent: Tuesday, 14 April 2020 23:13 To: bvaughn/react-window Cc: Nir Geller; Mention Subject: Re: [bvaughn/react-window] Support just-in-time measured content (#6)

@gnir-work ah... I don't need the 'scroll to row' functionality I think.. though maybe I'm wrong. — You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub, or unsubscribe.

-- This email has been checked for viruses by Avast antivirus software. https://www.avast.com/antivirus

kurochkinSergei commented 4 years ago

As a workaround if:

you can use CanvasRenderingContext2D.measureText() tо calculate row height instead of rendering whole component :poop:

CodeSandbox

edgars-sirokovs commented 4 years ago

@bvaughn Any plans on publishing a new alpha version on npm? We haven't had any update since 1.6.0-alpha.1 😕

mihanizm56 commented 4 years ago

@bvaughn any updates? will it be fixed in recent versions?

WillSquire commented 4 years ago

For those wanting to use Typescript with DynamicSizeList - module augmentation can give the types needed for DynamicSizeList working off of the DefinitelyTyped definitions (a workaround to my query posted in this thread last year).

Add the follow to the project:

import React, { Component } from 'react'

declare module 'react-window' {
  export type DynamicSizeListProps = Omit<FixedSizeListProps, 'itemSize' | 'scrollToItem'>
  export class DynamicSizeList extends Component<DynamicSizeListProps> {}
}

Then import as usual:

import {
  DynamicSizeList,
  DynamicSizeListProps,
} from 'react-window'

// use as normal...

@types/react-window still needs to be installed first for the FixedSizeListProps definition.

tony-scio commented 4 years ago

Thanks for the typedefs, going to add them now! I've been using DynamicSizeList in prod for over a year now. Currently based on this fork: "@john-osullivan/react-window-dynamic-fork": "^1.9.0-alpha.1" (is that the most up to date thing still?). I tried to switch to react-virtuoso because of the uncertainty about if/when this would actually be released. But found it less performant and stuck w/ this.

@bvaughn is it time yet to pave the cowpath and release this to npm? Maybe just renamed to ExperimentalDynamicSizeList if you're still worried about it not being ready.

slavafomin commented 4 years ago

Hello guys! Thank you for your hard work and this great library. I'm really looking forward for this feature! However, I would suggest to add information regarding this issue to the README and documentation/examples. It took me quite some time to figure out that dynamic content is not actually supported by the library and it's only useful for items with fixed/known size. I believe the README has a nice FAQ section where this could be added.

nihgwu commented 4 years ago

if you are looking for a table/grid/tree, this would be a good start https://autodesk.github.io/react-base-table/examples/dynamic-row-heights, built on VariableSizeGrid

anton6 commented 3 years ago

@tony-scio can you please share a working codesandbox for using DynamicSizeList from 1.6.0-alpha.1 with InfiniteLoader? Thanks, I would really appreciate it.

niklaspollonen commented 3 years ago

Has anyone gotten this to work with react-beautiful-dnd? Dragging seems to make the items jump on top of each-other.

andrei9669 commented 3 years ago

Has anyone gotten this to work with react-beautiful-dnd? Dragging seems to make the items jump on top of each-other.

i will also wait for this answer :D

eyea commented 3 years ago

[HELP] There is no code for show at this link: dynamic-size-list-vertical and I need this ability. THANKS

eyea commented 3 years ago

[HELP] There is no code for show at this link: dynamic-size-list-vertical and I need this ability. THANKS

https://react-window-next.now.sh/#/examples/list/dynamic-size

j-lee8 commented 3 years ago

Any update on this? We need something similar as we can (it's usually very few, but in some cases can be a lot) render a large responsive grid list of items (their heights are dynamic and will change on mobile as various text wraps etc). I feel react-window would be a game changer if it could handle this use case. If not, are there any reliable alternatives?

petyosi commented 3 years ago

Any update on this? We need something similar as we can (it's usually very few, but in some cases can be a lot) render a large responsive grid list of items (their heights are dynamic and will change on mobile as various text wraps etc). I feel react-window would be a game changer if it could handle this use case. If not, are there any reliable alternatives?

@JavaJamie since you specifically asked for alternatives - the react-virtuoso library comes with built-in support for measuring dynamic content. Disclaimer: I am the author of it.

rafde commented 3 years ago

@JavaJamie since you specifically asked for alternatives - the react-virtuoso library comes with built-in support for measuring dynamic content. Disclaimer: I am the author of it.

react-virtuoso is also double the size of react-window. Always have to keep dependency size in mind.

https://bundlephobia.com/result?p=react-window@1.8.6 https://bundlephobia.com/result?p=react-virtuoso@0.20.1

bvaughn commented 3 years ago

I have accepted the fact that I don't have the time or energy to finish this effort. If anyone would like to step in and finish the branch I started, I'd welcome your help. (Please also see issue #302 for how this would fit into version two release as the new List and Grid components.)

burtonator commented 3 years ago

We ended up implementing something from scratch for this such that used visibility sensors which worked out really well.

I could probably OSS it or point you guys to the right place if you want to rip it out and create a new project or move it here.

One trick is we used 'blocks' so that we could increase performance. Basically we take each row, and actually put it into a parent block of like 25 items, then swap that in when necessary.

plandem commented 3 years ago

@burtonator it would be really helpful

ofrinevo commented 3 years ago

Can anyone provide a bug-free implementation of react-select with DynamicSizeList? I can't get it to work well together. Currently I face 2 problems (sorry for no screenshots):

  1. All items have style={position: absolute, left: 0, top: 0} which doesn't let me use this style since all options sit on top of each other.
  2. If I don't use the style prop, the list is shown well, but when I scroll a bit, the part of the list with options in it shrinks, while the total height remains unchanged. Thus, when I scroll, I get x pixels of actual options and length-x pixels white space.

Could not find any working example of the two. I'll note that I use the 1.9.0 fork, on Chrome.

Edit. Found an answer here above, in the hidden comments section. https://github.com/bvaughn/react-window/issues/6#issuecomment-509016422

twall commented 3 years ago

One simplification to the scrolling issue would be to use the scrollbar to indicate the index of the item in the list instead of a pixel position, if the scroll is more than roughly one viewport height away. Instead of attempting to "scroll down" X pixels, the component simply renders the items around the desired index. Halfway down renders item at index N/2, and at bottom renders item at index N-1.

That would allow direct thumb scrolling to the middle or end of the list, without the lagging of the scroll thumb while the component renders and calculates sizes. Right now, for very long VariableSizeList components, it's nearly impossible to scroll to the bottom as the cursor drags faster than the scroll thumb moves.

Karpengold commented 3 years ago

Is here something in progress or this feature is freezed?

mimiqkz commented 3 years ago

I want to use this feature because Lighthouse keeps complain about Excessive DOM. However, it has been so many years now. Is there any alternation towards this? Another project? Since the author doesn't have much time and effort to continue this?

gmaclennan commented 3 years ago

react-virtual might solve this problem for some people here. It supports dynamic measurement rendering.

codebex commented 3 years ago

react-virtuoso is another option that has this feature.

tawtsvenz commented 2 years ago

Would you take a look at this sandbox, I tried to create a DynamicSizeList based on VariableSizeList https://codesandbox.io/s/dynamic-size-list-zcntp

KosGrillis commented 2 years ago

Any progress on this one?

Tainan404 commented 1 year ago

Any update?

inokawa commented 1 year ago

Hello. https://github.com/inokawa/virtua might help you if you want to virtualize dynamic content out-of-the-box.