I'm trying to use ref to set the scroll position when data is available. I believe this broke when upgrading to React 16.8.1.
Minimal example: https://codesandbox.io/s/8p7yy2xw2
You aren't really meant to use a ref to set scroll offset. There are public API methods for setting the scroll position (or specifying an element to scroll to).
Looks like I misunderstood what you were saying. That being said, I don't understand your repro case. You don't seem to be telling the list to scroll at any point.
You attach a ref, but then never touch it for anything.
Please provide a working repro case and I'll re-open and take another look.
I've expanded the repro to my usecase: https://codesandbox.io/s/8p7yy2xw2
It's weird that the ref.current is only actually set once entered in the effect. I managed to fix my issue by correctly setting the dependency array.
You can keep the ticket closed. Thanks!
It's weird that the
ref.currentis only actually set once entered in the effect.
I'm not sure what you mean by this.
By the way, you should change this: [ref.current, test, o] to this: [ref, test, o] 😄
I put in some console.logs to show what I mean. Anyway it's clear I don't yet fully understand the flow of execution between hooks and references. Thanks a lot for replying!
The logs show that your component renders twice (once with test false and once true) and your effect handler is called twice (once after each render). The first time it's called, the list ref is null– because you don't actually render the list when test is fail:

The next time it's called, the ref points at the newly rendered list. 😄
Thanks a lot for the replies, it helped me gain more insights into what's going on.
I have another question which is bogging my mind a bit; https://codesandbox.io/s/kx1wp6nyov
When you click the list container, it flips the state (and therefore creates a new render closure) which causes the row elements to unmount and remount. Why? Isn't the resulting React tree the same? How to prevent this unmounting/remounting?
Thanks a lot!
The "React tree" is built up of components– including function components like the inline renderRow function. Part of how React "diffs this tree includes comparing the _type_ of each node to see if it's changed. If it has changed, React assumes that everything under that node has changed (or at least that it's unsafe to assume it hasn't changed)– so it recreates that part of the tree. You can read more about this here.
In your case, if you want to avoid this, you need to keep the type stable between renders. You can do this by moving rowRenderer _outside of the body of_ Example and using the itemData prop to share the test value between them.
Like so:
https://codesandbox.io/s/5wknop1wx4
Thanks a lot! I managed to significantly improve performance in our application by using itemData.
However I'm still not convinced by your explanation. I created a minimum sandbox to show what I mean: https://codesandbox.io/s/7v572655q
In the app, you can click the container to increment the count. This creates a new renderTest function similar to the rowRenderer before. But in this example, the Child component does not remount (see console log). So it seems like React keeps the component tree intact. What do you think?
Thanks a lot for putting in effort to help me understand!
There's a difference between how renderChild in your example and rowRenderer are being used. In your sandbox. you call renderChild directly, which will execute in place and return a Child element. What's returned from Parent's render ultimately looks like this:
<div><Child num={someNumber} /></div>
If you check where react-window uses the children prop, you'll see it's using React.createElement and passing in rowRenderer as the type. If we update your sanbox to use React.createElement instead of calling the renderChild directly, what is returned from Parent's render would look (roughly) like this:
<div><renderChild num={someNumber} /></div>
The big idea is that renderChild(props) and <renderChild {...props} /> look the same, but are handled very differently by React. One executes in place, just like any other function call; React doesn't know about it because all it sees is what's returned from render. The other hands off execution to React, and in doing so is treated as an actual component.
The kind of "aha" thing to remember is that <Component {...props} /> is syntactic sugar for React.createElement(Component, props), which ultimately becomes an object kinda like { type: Component, props: props }. Notice Component isn't called, we just wrapped it up with the props we want it to be called with and return that. React knows what to do with it and takes care of calling it sometime later.
I hope I explained that alright!
That's a nice explanation, @heyimalex.
Perfect! It all makes sense. Thanks a lot for the detailed reply, @heyimalex. And thanks to @bvaughn for letting me use this thread.
Most helpful comment
The logs show that your component renders twice (once with

testfalse and once true) and your effect handler is called twice (once after each render). The first time it's called, the list ref is null– because you don't actually render the list whentestis fail:The next time it's called, the ref points at the newly rendered list. 😄