Mugenor
Mugenor

Reputation: 119

How can I dynamically calculate row height by row index for VariableSizeList?

I need to render long list via react-window library. I can't be sure that all rows will be the same size so it forces me to use VariableSizeList. So is there a way to calculate row height when you know only index of row and actual data to be rendered?

I think it's possible to render it somehow not visible to the end user and get height from but I'm not sure is it the right way.

Upvotes: 2

Views: 3998

Answers (2)

Dolphin
Dolphin

Reputation: 38671

I am facing the same issue, you can use the itemSize to specify the item height. For me I specify the size like this:

 <VariableSizeList
            ref={virtualListRef}
            width={width}
            height={height}
            estimatedItemSize={500}
            initialScrollOffset={getInitialScrollOffset()}
            itemCount={pdf.numPages}
            overscanCount={0}
            onScroll={(e: ListOnScrollProps) => handleWindowPdfScroll(e)}
            itemSize={(rowIndex) => getPageHeight(rowIndex, width)}
          >
 </VariableSizeList>

and this is the getPageHeight function:

 const getPageHeight = (rowIndex: number, width: number) => {
      if (!pageViewports) {
        throw new Error("getPageHeight() called too early");
      }
      const pageViewport = pageViewports[rowIndex];
      const scale = width / pageViewport.width;
      const actualHeight = pageViewport.height * scale * projAttribute.pdfScale;
      return actualHeight + 10;
    };

Upvotes: 0

Tom
Tom

Reputation: 6709

Yes when using VariableSizeList specify an itemSize function.

eg

itemSize = {(i) => onGetItemSize(rows[i])}

The onGetItemSize measures the content by:

  1. returning a single line height, if content is small.
  2. writing the content to a empty element, with the same width and properties as your row. Then measuring the element height, before emptying the element again.

For example:

 onGetItemSize={((row) =>
                {
                let text = row.original.text;
                // if no text, or text is short, don't bother measuring.
                if (!text || text.length < 15)
                    return rowHeight;

                // attempt to measure height by writting text to a, kind of hidden element.
                let hiddenElement = document.getElementById(this.hiddenFieldName());
                if (hiddenElement)
                {
                    hiddenElement.textContent = text;
                    let ret = hiddenElement.offsetHeight;
                    hiddenElement.textContent = '';

                    if (ret > 0)
                        return Math.max(ret, rowHeight);
                }

                // fallback
                return rowHeight;
            })}

Then include this empty element at an appropriate place in the DOM.

<div id={this.hiddenFieldName()} style={{ visibility: 'visible', whiteSpace: 'normal' }} />

Upvotes: 1

Related Questions