React-virtualized: Must InfiniteLoader's loadMoreRows use promises?

Created on 16 Mar 2018  路  9Comments  路  Source: bvaughn/react-virtualized

The InfiniteLoader List example says to add at least 1 to the rowCount if there's more to load. This is needed for it to trigger the loading. Unfortunately, it also causes the list to attempt to render a row even though the data is not there. Then, once the data shows up, that row does not get re-rendered until much later, when you stop scrolling with that row in view. This leaves a noticeable gap in your list.

Here's my repro case. Keep scrolling steadily without stopping and you'll notice that the numbers 20 and 21 do not show up unless you let your scrolling hit a dead stop. Ditto for 40 and 41. This is because I'm loading items in chunks of 20 and adding 2 rows of buffer.

I tried a bunch of workarounds for this:

  • I tried making isRowLoaded return true or false for this row, but it made no difference.
  • I tried simply not returning a value from renderRow, but that caused my scroll position to jump back to the top of the document when I reached it.
  • I tried passing a rowCount that includes the extra row to the <InfiniteLoader> and a rowCount that does not include the extra row to the <List>. This worked! It seems wrong though. Is this really OK to do?

To be more clear, my work-around looks something like this:

<InfiniteLoader rowCount={loadedItems + 1} ...>
  ...
  <List rowCount={loadedItems} .../>
  ...
</Infiniteloader>
question

Most helpful comment

If anyone uses redux-saga or some other more complicated redux side-effects management, I had great success using https://github.com/erikras/redux-promise-listener to turn my dispatched actions into a Promise that InfiniteScroller can use.

All 9 comments

I suppose I could have made my example more clear by rendering "Loading..." instead of the empty string in those rows. That's the real issue here. List tries to render them before the data is loaded, so I render a placeholder, but then it doesn't re-render them when the data becomes available. It only renders them when you stop scrolling.

Is this a case where I'm supposed to use forceUpdateGrid? But when and how would I call that, since the data is all coming in from props? It sure would be nice if InfiniteLoader could handle this for me automatically.

Oh. There's a comment about this in the code already. I guess I have to return a promise from loadMoreRows to make this work correctly? I guess that makes sense, and that's how it's explained in the documentation now that I take a closer look at it.

However, my app wasn't written with promises. It uses Redux. My loadMoreRows function is a dispatched Thunk. Data comes back in through props. Is there a way to make this feature work without promises?

as a workaround, you could have your loadMoreRows function return a Promise that you resolve in componentWillReceiveProps when you determine that the new rows have been added. it's not the cleanest solution but should fix your problem in the short term

To solve this we had to specifically add a callback property to the initial action so that the eventual resolution/failure could be signaled back to the caller.

This is going to be a way more broad problem with the _suspend_ feature of React 17 since it will involve _throwing a promise_, which we can鈥檛 create out of thin air in redux.

@aem approach is not that bad actually. We could keep track of all the requested rows with a deferred that we can resolve later.

I just realized that when Redux dispatches a thunk it returns whatever that thunk returns, so I could just return a promise from the thunk.

If anyone uses redux-saga or some other more complicated redux side-effects management, I had great success using https://github.com/erikras/redux-promise-listener to turn my dispatched actions into a Promise that InfiniteScroller can use.

@aem How would you handle the promises in componentWillReceiveProps? I'm trying to work out how to trigger the promise resolution once a prop is populated. (My team is using flux, so I can't hook into redux-thunk or redux-saga.)

Nevermind, looks like InfiniteLoader just calls forceUpdateReactVirtualizedComponent when the promise is resolved, so I'm going to call that directly when the data prop changes (no promises required).

Was this page helpful?
0 / 5 - 0 ratings