3

I am building a React application, I have multiple Grid components, combined with ScrollSync, like this:

<ScrollSync>
...
   <Grid />
   <Grid />
...
</ScrollSync>

It looks something like this:

enter image description here

They are scrolling correctly, one is following another. BUT, I need GRID 1 to be fully rendered in DOM, always.

Grid's overscanRowCount option isn't helping, as it only gives me n number of rows, but only in direction of scrolling.

For example if I have 10 rows rendered, and I have set overscanRowCount to 6 - if I am scrolling down I will have 10 + bottom 6 rows, if I am scrolling to top, I will have 10 + 6 top rows.

Why do I need it? My app looks like this:

enter image description here

So, when for example "prod5" item goes out of the DOM, that blue line goes missing too, every line is going from row item to the top.

Good option to me would be to have n top AND bottom rows rendered, or simply - all of them. Any solutions or workarounds? I searched a lot, couldn't find anything helpful.

Saugat Bhattarai
  • 2,614
  • 4
  • 24
  • 33
Tinmar
  • 370
  • 4
  • 23
  • Why do you need it rendered always? What problem are you trying to solve? – Jordan Running Mar 13 '17 at 14:31
  • I updated the question. – Tinmar Mar 13 '17 at 14:47
  • 1
    One possibility: Don't use react-virtualized for the left grid, and [implement ScrollSync yourself](https://github.com/bvaughn/react-virtualized/blob/77cd7b78cf137a8a89bbc95d9d9d8ec2bebfcd37/source/ScrollSync/ScrollSync.js). – Jordan Running Mar 13 '17 at 15:27
  • 1
    Another possibility: Render the blue line as segments (one segment per cell) and pre-process your data in such a way that the props passed to each cell include an e.g. `hasBlueLineSegment` property. – Jordan Running Mar 13 '17 at 15:30
  • First possibility: I could give it a try, thanks. Second: I am actually asking question on SO so I wouldn't have to do that... It would require major application refactoring, I will do that only if everything else fails. Thanks for help. – Tinmar Mar 13 '17 at 15:33
  • 1
    Have you looked at the [`overscanIndicesGetter` prop](https://github.com/bvaughn/react-virtualized/blob/77cd7b78cf137a8a89bbc95d9d9d8ec2bebfcd37/docs/Grid.md#overscanindicesgetter)? If you know the total number of cells it seems like you could implement a function that always returns `{ overscanStartIndex: 0, overscanStopIndex: totalNumberOfCells }`. – Jordan Running Mar 13 '17 at 15:44
  • Yes, I discovered it literally 10 minutes ago. It isn't clear to me how to implement it, though. To copy default function, change it as I need, and pass it as `overscanIndicesGetter` prop? – Tinmar Mar 13 '17 at 15:46

1 Answers1

3

I'm not an expert on react-virtualized, but it seems that defining a custom overscanIndicesGetter function may the simplest solution. Per the docs:

overscanIndicesGetter

This is an advanced property. This function is responsible for calculating the number of cells to overscan before and after a specified range. By default, React Virtualized optimizes the number of cells to overscan based on scroll direction. If you'd like to customize this behavior, you may want to fork the defaultOverscanIndicesGetter function.

In the below snippet I've defined a function that always returns an overscanStartIndex of 0 and overscanStopIndex equal to the last index:

const overscanIndicesGetter = ({cellCount}) => ({
  overscanStartIndex: 0,
  overscanStopIndex: cellCount - 1,
});

Take a look in your developer tools and you'll see that it does in fact render all 1,000 cells.

const { Grid } = window.ReactVirtualized;

const data = Array.from(new Array(1000)).map((_, i) => i);

const Cell = ({key, rowIndex, style}) =>
  <div key={key} style={style}>{data[rowIndex]}</div>;

const overscanIndicesGetter = ({cellCount}) => ({
  overscanStartIndex: 0,
  overscanStopIndex: cellCount - 1,
});

const App = ({data}) => (
  <Grid
    cellRenderer={Cell}
    columnCount={1}
    columnWidth={100}
    rowCount={data.length}
    rowHeight={30}
    height={300}
    width={300}
    overscanIndicesGetter={overscanIndicesGetter}
  />
);

ReactDOM.render(<App data={data}/>, document.querySelector('div'));
<link rel="stylesheet" src="https://unpkg.com/react-virtualized@9.2.2/styles.css"></link>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.4.2/react-dom.min.js"></script>
<script src="https://unpkg.com/react-virtualized@9.2.2/dist/umd/react-virtualized.js"></script>
<div></div>

If you wanted, you could try to make this smarter. For example, if you're able to calculate the index of the next "prod" cell (i.e. the cell that determines whether or not visible cells have a blue line) based on the indices of the visible cells, you could modify the function to return that index for overscanStopIndex. The built-in defaultOverscanIndicesGetter function documents all of the values your function will receive.

/**
 * Calculates the number of cells to overscan before and after a specified range.
 * This function ensures that overscanning doesn't exceed the available cells.
 *
 * @param cellCount Number of rows or columns in the current axis
 * @param scrollDirection One of SCROLL_DIRECTION_BACKWARD or SCROLL_DIRECTION_FORWARD
 * @param overscanCellsCount Maximum number of cells to over-render in either direction
 * @param startIndex Begin of range of visible cells
 * @param stopIndex End of range of visible cells
 */

If you have a lot of data, it may be well worth your time to see what you can do with that.

Jordan Running
  • 102,619
  • 17
  • 182
  • 182
  • 2
    This is the correct answer for what you've described ^ Don't render all rows in your left Grid. It would be bad for performance. The indices getter should be the simplest solution for you. Alternately you could provide your own cellRangeRenderer that decorates the default and manages your connecting lines. There is a basic example of extending that prop in the docs as well. – bvaughn Mar 13 '17 at 20:04
  • It works, and great advice for making it smarter too. I will let it run like this in first version of application - so far performance is great. It's way more important for right grid to NOT BE in DOM, left grid is not a problem, there aren't that many items. Some day, I will implement calculating of specific number needed for blue lines to be visible. Thanks again! – Tinmar Mar 14 '17 at 17:06