Skip to content Skip to sidebar Skip to footer

React-virtualized Table To Grow Up To A Maximal Height

Problem: I would like to have a React Virtualized table that can take up a maximal space in height (let's say 500px). Immediately below the last visible row of the table, I would l

Solution 1:

No need to resort to React or anything complicated. Just use FlexBox in simple CSS.

Give the container of your "Table" and "Other content" display: flex and set a max-height on the inner element that wraps the <table>. FlexBox children will try to expand to fill as much space as their contents require and no more, but won't expand past their max-height if set.

Note: Expand snippet to full screen to see the full effect.

functionadjustrows() {
  let val = document.getElementById("slider").value;
  let table = document.getElementById("table");
  let inner = `
    <tr>
      <th>foo</th>
      <th>bar</th>
      <th>baz</th>
      <th>foo</th>
      <th>bar</th>
      <th>baz</th>
    </tr>
  `;
  for (let i = 0; i < val; i++) {
    inner += `
      <tr>
        <td>foo</td>
        <td>bar</td>
        <td>baz</td>
        <td>foo</td>
        <td>bar</td>
        <td>baz</td>
      </tr>
    `;
  }
  table.innerHTML = inner;
  document.getElementById("numrows").innerText = val;
}
adjustrows();
table, tr, th, td {
  box-sizing: border-box;
  border: 1px solid gray;
}

table {
  flex-grow: 1;
}

th {
  background: lightgray;
}

label {
  display: flex;
  justify-content: center;
  margin: 5px;
}

#numrows {
  margin: 010px;
}

#flex-container {
  display: flex;
  border: 1px solid red;
  flex-direction: column;
}

#table-container {
  display: flex;
  max-height: 500px;
  border: 1px solid blue;
  padding: 2px;
  margin: 2px;
  overflow: scroll;
}

#other-content-container {
  display: flex;
  flex-direction: column;
  border: 1px solid green;
  padding: 2px;
  margin: 2px;
}

.placeholder {
  background: darkgray;
  margin: 10px;
  height: 120px;
}
<divid="flex-container"><label><strong>Number of table rows:<spanid="numrows"></span></strong><inputid="slider"onchange="adjustrows();"oninput="adjustrows();"type="range"min="0"max="100"value="1" /></label><divid="table-container"><tableid="table"></table></div><divid="other-content-container"><divclass="placeholder"></div><divclass="placeholder"></div><divclass="placeholder"></div><divclass="placeholder"></div><divclass="placeholder"></div></div></div>

Note that if you delete some of the <tr> elements from the table so that it is shorter than 500px, #table-container will simply shrink down to accommodate.

As always, Mozilla Developer Network has some pretty good info on FlexBox.

Solution 2:

I was able to accomplish this for React virtualized List using rowRenderer and refs, but I believe it should work with Table as well.

  1. Create state to contain the height of the list, make the default your intended max height:

const [listHeight, setListHeight] = useState(500);

  1. When rendering the List, use the height from this state:

<List height={listHeight} .../>

  1. Create a list of refs for each item in your list (in this case the elements are divs):

const rowRefs = useRef(dataInList.map(() => React.createRef<HTMLDivElement>()));

  1. On the rowRenderer, attach the refs to the DOM elements that are representing the rows in your table (I'm using cellMeasurer as well)
const rowRenderer = useCallback(
  ({ key, index, style, parent }: ListRowProps) => {
    const item = dataInList[index];
    return (
      <CellMeasurerkey={key}cache={cache.current}columnIndex={0}parent={parent}rowIndex={index}><divref={rowRefs.current[index]}>
              Your row info goes here 
          </div></CellMeasurer>
    );
  },
  [formatted]
);

  1. Create an effect to trigger when the data source changes (or possibly just once when the component mounts). The effect will sum the heights of the rows using the refs offsetHeight and set the state listHeight to that or whatever max height you want (whichever is less of the two).
useEffect(() => {
    const domListHeight = rowRefs.current
        .filter((e: any) => e.current)
        .reduce((acc: any, e: any) => acc + e.current.offsetHeight, 0);
    
    constMAX_HEIGHT = 500;
    const usableHeight = domListHeight < MAX_HEIGHT ? domListHeight : MAX_HEIGHT;
    
    setListHeight(usableHeight);
}, [dataInList]);

Summary: The component will render the list at the max height and then calculate the actual height of all the rows combined, if that total is less than the max height intended, the component will re-render the list from scratch with that new lower height.

Solution 3:

I was able to fix this by disabling height in Autosizer and calculating it manually like this:

const ROW_HEIGHT = 40const MAX_HEIGHT = 500
<AutoSizer disableHeight>
  {({ width }) => (
    <Listwidth={width}height={Math.min(MAX_HEIGHT,ROW_HEIGHT * data.length)}
      rowCount={data.length}rowHeight={ROW_HEIGHT}rowRenderer={rowRenderer}overscanRowCount={10}
    />
  )}
</AutoSizer>

Post a Comment for "React-virtualized Table To Grow Up To A Maximal Height"