I am using the List component (8.4.1) for a very long list (2000+) of fairly complicated elements. I have started seeing painting issues but I haven't been able to pin-point the problem exactly. If I flick the scroll wheel on my mouse to zip down the list, then flick the opposite direction - sometimes - when I get to the top of the list, the top cells don't paint. The elements are in the dom and laid out on screen, I can click on the elements and inspect them - but scrolling no longer works and my list component is blank. Resizing the browser, or changing the data in the list seems to trigger a paint and everything is good.
I have tried adding a "refresh" property to my List, and setting it to today's Date at the end of the scroll to trigger a paint... this doesn't seem to have any effect. What DOES seem to have an effect (but adversely affects scrolling performance) is to override the css property will-change to "auto".
Sorry, I don't have code that reliably re-creates the problem.
Any ideas??
Sounds like a browser bug to me.
Don't think there's anything I can do to help with this in its current state. I've never seen it and there's no repro steps.
It does seem like a browser bug; I was mostly interested in knowing if you had seen it before. Thanks for the quick reply. Setting "will-change" to auto definitely fixes things for me, so I'm good. Thanks for your quick reply.
Can you provide a few more details about your browser version, operating system, etc? I can try to pass the word along.
Version 54.0.2840.71 (64-bit)
macOS 10.12
Several of my coworkers saw the same issue on various other versions of macOS and Chrome.
Here's a screenshot to show that the elements exist onscreen, they just aren't painting:

I'm running the same version of Chrome as you but on OSX 10.11.6. Can't reproduce the behavior you've described by doing what I think you're saying you're doing.
It would help if you could record a video of it happening. I don't know if the above report is enough detail to get any traction from the Chrome team.
@mcyze: Try this build? (Rename .zip to .tgz ~ Github won't allow me to upload a file with .tgz extension for some reason.)
Is the bug you reported gone? Is performance still okay?
@bvaughn After looking at this, I don't think this is the same issue that I'm seeing.
The one I noticed was that when I had the will-change: transform rule it would sometimes require two clicks in order to trigger the onClick event. However, it does seem to happen sporadically, usually after scrolling up and down and then attempting to click on one of my rows.
I changed the rule to will-change: auto, and that fixes the issue. It also doesn't seem to have a noticeable impact on performance.
The click issue is likely not at all related to will-change. I disable pointer-events while scrolling is in progress (for perf) and reenable them after a very short delay when scrolling stops. If you click too soon- before they're reenabled, the click will be ignored.
This is customizable behavior of you don't want it. It's in the docs:
scrollingResetTimeInterval: Wait this amount of time after the last scroll event before resetting Gridpointer-events; defaults to 150ms.
@bvaughn Oh, I see :smile:
And actually, it looks like disabling will-change did not actually fix the issue. I just wasn't scrolling quickly enough to reproduce the issue :stuck_out_tongue:
Thanks for the info!
Haha, that makes sense @maxdeviant. ๐
Pinging @mcyze ๐
@bvaughn Sorry about my lack of response, I am on vacation this week and purposefully haven't been checking my email. I will be back in the office first thing Monday morning. I appreciate your very quick turnaround and I will try your build asap on Monday.
Oh! No problem then. Sorry for disrupting your vacation. Enjoy your time off. ๐
Ping @mcyze ๐
@bvaughn I tried the 8.4.2 zip you shared and had the same issue. I emailed your gmail account with links to a couple videos that show what I am seeing.
Bummer. ๐ Thanks for sending the demo videos @mcyze.
I'm a bit surprised by that. The diff I applied in the patch I sent you was:
diff --git a/source/Collection/CollectionView.js b/source/Collection/CollectionView.js
index 31b0859..5a458bf 100644
--- a/source/Collection/CollectionView.js
+++ b/source/Collection/CollectionView.js
@@ -357,9 +357,9 @@ export default class CollectionView extends Component {
height: autoHeight ? 'auto' : height,
overflow: 'auto',
position: 'relative',
+ transform: 'translate3d(0,0,0)',
WebkitOverflowScrolling: 'touch',
- width,
- willChange: 'transform'
+ width
}
// Force browser to hide scrollbars when we know they aren't necessary.
diff --git a/source/Grid/Grid.js b/source/Grid/Grid.js
index 66e57f9..50fee08 100644
--- a/source/Grid/Grid.js
+++ b/source/Grid/Grid.js
@@ -511,9 +511,9 @@ export default class Grid extends Component {
direction: 'ltr',
height: autoHeight ? 'auto' : height,
position: 'relative',
+ transform: 'translate3d(0,0,0)',
width,
- WebkitOverflowScrolling: 'touch',
- willChange: 'transform'
+ WebkitOverflowScrolling: 'touch'
}
const totalColumnsWidth = this._columnSizeAndPositionManager.getTotalSize()
If will-change is what is causing your problems- I would expect that replacing it with a 3d transform would resolve the issue, _unless_ the real issue you're seeing has to deal with creating separate layers.
But if this were a common problem, I would have expected to have heard about it in the last year. Best I can tell, yours is the first report of it I've seen.
Hi guys, I've been experiencing issues that sound somewhat similar to those experienced by @mcyze
In my case, I am using a component called react-sortable-tree.
I'm not exactly sure where the issue lies with this problem, but I just thought I might link to the issue I've opened there as well.
If you'd like to help debug this, @ncphillips, I'd welcome your help. I haven't reproduce it myself so it's a bit difficult to work with.
I've also experienced something like this. Here's a video I took that shows two separate issues - one of them (when the screen goes completely blank) is a separate issue (fixed by #482) but you can also see flickering occurring while scrolling vertically, which sounds to me like the same issue described here.

In my brief experience attempting to fix this, I've found that setting pointer-events: auto !important on the body element immediately fixes it (i.e. if the grid was painted incorrectly and stuck in that state), and stops it from being painted incorrectly while scrolling again. But obviously that's not a solution I'm comfortable with :disappointed:
Note that my example is a actually a nested AutoSizer > WindowScroller > Grid, rather than a List, and the flickering occurs in Chrome 54.0.2840.99 on my Windows 7 work PC.
@bvaughn my issue was caused by the div surrounding the AutoSizer not having a set height. Because of this, height was 0, so the Grid didn't render anything.
@ncphillips That's a common problem. I mention it in the docs but I realize that the docs are kind of big and parts sometimes go unnoticed. ๐
@bvaughn haven't had time to dig in to debug nor time to grok whether the workarounds discussed here are relevant, and you might prefer we open a new issue, but like @bradchristensen mentioned above, I also see "flickering occurring while scrolling vertical" but I only see it with a scroll wheel on a mouse. If I use page up/down or home/end, no flickering. A co-worker tells me using a trackpad they don't see flickering either. My browser Chrome (54.0.2840.98) on Mac.
I see this in all the demos on the react-virtualized site. It's especially apparent when cells have background colors (which I did in my own test app, but it has data I can't record, so that's not shown in the video below).
A first theory was maybe this was Chrome's doing, but included in the second half of the linked video is a different react virtual listview impl that doesn't have the flickering (and it has row background colors), so that ruled out it being Chrome's fault alone.
Here is the video repro of this happening on the demo site; and not happening in another implementation (https://www.dropbox.com/s/wdos2rcj3hte87b/react-virtualized-glitching.mov?dl=0). (Note: I realize the 'scrolling' comes in/out by design).
(Wish i had time to dig in more, but figured I'd share what I know at the moment.)
@tonygrue unless I'm missing something, it looks like you are scrolling all the way from the top to the bottom of the list in just one or two scrolls, which would be too quick for virtual rendering to keep up with as it typically only pre-renders a few extra items for overscanning. I don't experience this problem myself with any of the examples on the demo site, so is it worth checking that your mouse wheel isn't set to scroll a bunch of lines or a whole page at once?
From what I can tell, scrolling with the mouse wheel in Chrome occurs asynchronously, meaning the rendering will catch up during or after scrolling, whereas scrolling by dragging the scrollbar occurs synchronously and so the new content is rendered before the scrollbar has moved to its new position. That could explain why you only have this problem with the mouse wheel, although I'm not sure why it would work in one virtual list impl but not the other.
I can reproduce this issue with the following code: https://jsfiddle.net/bradchristensen/vd49b6e5/
Scroll to the bottom of the grid, scroll some way across horizontally, then scroll upwards again. Usually I see that the grid is painted incorrectly when I stop scrolling, whether using the mouse wheel or the scrollbar.
Note that the above repro is using a patched version of react-virtualized from https://github.com/bvaughn/react-virtualized/pull/482 in order to get the rows to render correctly when scrolling horizontally, but you can also reproduce it with https://jsfiddle.net/bradchristensen/cj22s6gr/ (you just initially won't see the rows at the bottom of the grid after you've scrolled horizontally, until you start scrolling back upwards).
e.g. (in Windows 10 with Chrome 54.0.2840.99):

@bradchristensen and others. yeah i was definitely scrolling large distances (trying to stress the list view to see how it compares to non-web platform implementations, perf wise). the issue was as you say that when scrolling with the mouse wheel, chrome scrolls and renders asynchronously; so even though I can generate rows ate >= 60fps, Chrome is one frame behind. Since it's possible for my users to scroll faster than any reasonable overscan amount, I'm a little out of luck. One break I had, is I did find a Chrome flag 'Threaded scrolling' that when disabled returns scrolling to the main thread, enabling me to get 60fps and no glitching/flashing. It's a workaround for me if I control the webview (which in one of my projects I do). Reading Chrome source it looks like there might be other scenarios that prompt Chrome to do scrolling on the main thread (I don't come from a web background, so I'll need to learn more about CSS to learn how to use them). And it looks like Chrome itself scrolls on the main during scroll 'animations' which I presume is why I see no glitching/flashing with home/end/page_up/page_down etc.
Anyway, just wanted to close the loop on my comment, I'll let this thread get back to it's original purpose. Sorry for the distraction.
Hey @bradchristensen, first of all, thanks for providing a repro case.
Thanks to it, I was able to replicate the same issue on Chrome 54 on a Mac using a trackpad. It's not quite an exact science, but I was generally able to reproduce the same issue after fiddling around with it for a while following the instructions you suggested (scrolling all the way to the bottom of the list, and then back up a little, and then from left to right)
Here's a screenshot of what it looks like on my end. The element is in the DOM, has the correct dimensions and there isn't any special styling applied that would cause it to be invisible:

I believe this to be a browser rendering issue, and it does indeed only appear to happen when the grid is forced into hardware acceleration (via will-change: transform; or transform: translate3d(0, 0, 0)). I think the reason why that same bug doesn't happen without it is because disabling that optimization forces the entire grid to be re-painted by the browser each time a cell is added or removed.
One alternate solution I can think of to solve this without having to fall-back on removing will-change (and repainting the entire grid every time a new cell is added) is to force the individual cells into hardware acceleration as well. You can do so by applying transform: translate3d(0,0,0) to the cells directly.
Can you look at this fiddle and let me know if the issue still occurs on your end even when the cells are forced into hardware acceleration? https://jsfiddle.net/vd49b6e5/3/ It did seem to solve the issue on my end, as I was unable to replicate the issue after applying that fix.
I haven't fully looked into the performance implications this has though, so I don't think that that's a fix we'd want applied by default at this point (perhaps something to be mentioned in the docs instead).
Awesome @clauderic - that does seem to do the trick, however I think you're also right about the performance implications unfortunately. It looks like that introduces quite a lot of overhead in painting... at least in relative terms. In a not-particularly-scientific test, I scrolled one notch on the scroll wheel three times in each fiddle (with and without the transform fix) and profiled the painting. Zooming in as far as possible on the timeline, it looks like with the cells in hardware acceleration too, each cell is individually painted, as well as the top and bottom blank areas, and several milliseconds are then spent compositing those layers. Without the fix, it seems as if the paint only occurs once and only what's visible on screen gets painted - compositing layers then only takes up to 0.2ms. This isn't a noticeable impact to my eye but Chrome actually shows that it's rendering at about 35-40fps on the left, instead of the mostly 60+fps on the right.
Here's a screenshot of the paint profiles side by side (with the fix on the left), each filtered to show only one scroll notch, and without the impact of pointer events being reset a moment later. You can also see that the pattern continues in the following scrolls.

From what I can tell, scrolling with the mouse wheel in Chrome occurs asynchronously, meaning the rendering will catch up during or after scrolling, whereas scrolling by dragging the scrollbar occurs synchronously and so the new content is rendered before the scrollbar has moved to its new position.
Just chiming in here to mention that async scrolling is _very good_ for performance and helping an application feel responsive even when there's a lot going on in the UI thread. The downside is that- with windowing libs like this- the user can scroll "ahead" of where JavaScript is- but this is better than the alternative (the browser grinding to a halt)).
Dragging the scroll handle is synchronous, as mentioned, and as a result...it can really stress a lib like RV (in its current implementation at least) as it tries to chew through all of the rows in between point A and B.
@bradchristensen I suspected that might be the case. Would you mind comparing it to having will-change: transform disabled completely?
That way you'll be able to compare which is more detrimental to performance (forcing cells into hardware acceleration vs having the entire grid re-paint every time a cell is added/removed)
Removing will-change: transform does seem to have fixed it for me, and interestingly has almost identical performance to the original (unmodified) react-virtualized. Note that I just modified the element style in the dev console of the browser to get this result.

Can confirm that I was also able to repro the blank rendering in Chrome with the test case mentioned above and that removing the will-change style "fixed" it.
Any news on this? It causes (or at least contributes to) the scrollbar appearing behind rows that have a background color:

This issue occurs for us in (non-exhaustive) Chrome Version 55.0.2883.95 (64-bit) on macOS Sierra 10.12.2 (16C68), and Safari 10.0.3 (12602.4.8) as well as (12602.3.12.0.1).
In our small sample size (3 developers with slightly different software versions), the issue is _not_ present in the same version of Chrome on macOS Sierra 10.12.2 (16C67).
I created a CodePen with just the CSS necessary to reproduce: http://codepen.io/anon/pen/pRjGNy
No update.
FWIW the bug doesn't repro for me, also with Chrome 55.0.2883.95 (64-bit), macOS 10.12.2 (16C67).
@bvaughn we've been doing some testing with react-virtualized to get it into GitKraken. We noticed the tearing similar to what is mentioned in this thread in our app only on Windows and Linux. It does not happen in OSX.
Our repo steps is that we are using ScrollSync wrapped around 3 columns which all have their onScroll and scrollTop set. What we saw was the column that had the event sent to it would call setState and then call its onScroll (i.e. it gets set twice). This led to the 2 other panels getting out of sync and the panel that received the mouse scroll event to get a strange tearing effect.
After removing the setState and letting the ScrollSync change the scrollTop of all of the panels (i.e. 1 setState for all panels instead of 2 setStates for the panel with the mouse over it and 1 setState for all other panels) the lagging and tearing issues went away.
This might be a fix for the tearing issues also seen here. I can submit a PR that others can test if you want.
FWIW we were using transform3d but performance is far superior with the will-change implementation in this library.
So there's something else still going on. I'm going to dig into it more tomorrow and report back. One thing that for sure fixes it on all of our machines is putting a Grid as an overlay on top of the whole scrollable area that handles the onScroll. Unfortunately that also absorbs all mouse events so that doesn't work either.
Yup, have considered that from a few angles but without any luck.
Your help looking into this is much appreciated!
@bvaughn @implausible has a fix. We will be PR'ing soon. Turns out it's the mouse wheel event that was making things weird.
The wheel event? RV doesn't use the wheel event. Or do you mean the wheel event in application code?
@bvaughn RV doesn't use the wheel event but what happens is that when will-change: transform is set then chrome (only on linux/windows) will scroll the dom element under the mouse wheel independently from the JS thread. You can even set a breakpoint in JS and still scroll the element (?!?!?!?). People affected by this should see if they still experience the same behavior if they use the scrollbar instead. That should not give the same result if we are correct.
This behavior is stopped when you set an onWheel listener and event.preventDefault() but then you have to handle the event yourself.
This does not happen on OSX. It behaves exactly how one would expect.
@implausible has a fix for this that we are going to be using. He can open a PR if you'd like to at least see what he did.
I would love to take a look at the fix @implausible has come up with. Thank you for offering to share it. ๐
I'm a bit confused though. JS runs in the main thread. Chrome (since v49 I think?) manages scrolling and other user inputs in a separate thread so that input isn't blocked by JS (source):
By routing all input events to the impl thread before passing them to the main thread, we can scroll and redraw the tree without ever consulting the main thread. This allows us to implement โsmooth scrollingโ even when the main thread is blocked.
I _think_ the fact that the scrollbar produces different behavior is that it's processed synchronously. I don't have a source for that but I think it's the case. Wheel events are synchronous as well which is why they behave differently from scroll events. (RV avoids wheel events for this reason; they're bad for perf.)
Yeah, that definitely seems to be aligning with what we're seeing. The onWheel has a throttle of 60 fps on it which is why it's still fast. Looking at the profiler we're getting similar perf on various machines using this strategy. requestAnimationFrame did not work which is weird. The throttle did. ยฏ_(ใ)_/ยฏ
Chrome must put the onWheel into the JS thread somehow since this fixes it and stops it being async.
I think the ideal is that chrome fixes its stuff but this is a workaround at least for the moment.
I was previously working on another virtualizer for React, and ran into some _very_ peculiar behavior in regards to using requestAnimationFrame in the onWheel event. Throttling to 60fps seems to provide the same scroll performance across machines.
I _think_ wheel event sucks when running on under-powered hardware _or_ when cells are doing a lot of heavy rendering- scrolling feels jerky.
I don't think I'm ready to make a PR right now, but the changes I'm proposing are found: https://github.com/implausible/react-virtualized/blob/fix/mouse-wheel-tear/source/Grid/Grid.js#L925.
This performs close to the same perf on our slowest box.
Yeah, we saw the same as soon as we threw on the listener on underpowered hardware. After the throttle we couldn't tell a difference. Haven't tried on mobile however but they shouldn't be getting an onWheel event.
When you say "throttle" are you referring to debouncing your wheel events?
No. By throttle I mean that we only call our wheel event once every period of time. In our case, we're limiting the call to our wheel event to once every 1000 / 60 ms
Typical debouncing is a sliding window, meaning we won't call our event until we haven't received any new events in a period of time.
Sorry, poor choice of words on my part. What you're describing is what I was assuming. Just wanted to verify that I wasn't off. ๐
The difference between debounce and throttle still escapes me frequently.
๐ Words are hard.
The article lodash links is always a great refresher though. Looks like they've updated it further with discussions on rAF vs debounce vs throttle as well. https://css-tricks.com/debouncing-throttling-explained-examples
Still doesn't explain why rAF was so much worse than _.throttle :(
I'm having the same problem when I render Lists inside Grid/Collection. Either of these components work well in separation. Disabling will-change: transform; on Lists stops the flickering. Flickering happens in the deeper component (List) only when it's scrolled.

Not sure if it's the same issue, but I already reported similar issue related to will-change to chromium guys (is fixed by now, but not in stable Chrome/Chromium yet).
Looks like it's fixed https://bugs.chromium.org/p/chromium/issues/detail?id=700013#c10
@johnhaley81 like I said, I'm not sure if it's the same issue, but yes that one is fixed, but probably not in mainline Chrome/Chromium yet. Have to wait. And then there is Firefox and Edge too.
Haven't heard anything about this issue for a few weeks, since mentions of a fix. Going to take a chance and close it now. We'll re-open it if it pops back up.
Most helpful comment
Can confirm that I was also able to repro the blank rendering in Chrome with the test case mentioned above and that removing the
will-changestyle "fixed" it.