Skip to content

Usage with react-table? #10

Closed
Closed

Description

@deadcoder0904

I cloned the react-table's Simple sandbox to get react-virtual working with it.

But I'm confused what to put instead of rowVirtualizer.virtualItems when using .map() inside tbody?

Also, confused if I have to wrap the whole table or just tbody as it's the only thing that needs scrolling.

I think all the rows get rendered because the rendering is too slow for only 2000 items. I'm sure I must be doing something wrong.

Here's my CodeSandbox 👉 https://codesandbox.io/s/react-table-basic-with-react-virtual-tq047?file=/src/App.js

And here's the same code if I ever delete it:

import React from "react";
import styled from "styled-components";
import { useTable } from "react-table";
import { useVirtual } from "react-virtual";

import makeData from "./makeData";

const Styles = styled.div`
  padding: 1rem;

  table {
    border-spacing: 0;
    border: 1px solid black;

    tr {
      :last-child {
        td {
          border-bottom: 0;
        }
      }
    }

    th,
    td {
      margin: 0;
      padding: 0.5rem;
      border-bottom: 1px solid black;
      border-right: 1px solid black;

      :last-child {
        border-right: 0;
      }
    }
  }
`;

function Table({ columns, data }) {
  // Use the state and functions returned from useTable to build your UI
  const {
    getTableProps,
    getTableBodyProps,
    headerGroups,
    rows,
    prepareRow
  } = useTable({
    columns,
    data
  });

  const parentRef = React.useRef();
  const rowVirtualizer = useVirtual({
    size: rows.length,
    parentRef,
    estimateSize: React.useCallback(i => rows[i], []),
    overscan: 5
  });

  // Render the UI for your table
  return (
    <div
      ref={parentRef}
      className="List"
      style={{
        height: `450px`,
        width: `650px`,
        overflow: "auto"
      }}
    >
      <div
        className="ListInner"
        style={{
          height: `${rowVirtualizer.totalSize}px`,
          width: "100%",
          position: "relative"
        }}
      >
        <table {...getTableProps()}>
          <thead>
            {headerGroups.map(headerGroup => (
              <tr {...headerGroup.getHeaderGroupProps()}>
                {headerGroup.headers.map(column => (
                  <th {...column.getHeaderProps()}>
                    {column.render("Header")}
                  </th>
                ))}
              </tr>
            ))}
          </thead>
          <tbody {...getTableBodyProps()}>
            {rows.map((row, i) => {
              prepareRow(row);
              return (
                <tr {...row.getRowProps()}>
                  {row.cells.map(cell => {
                    return (
                      <td {...cell.getCellProps()}>{cell.render("Cell")}</td>
                    );
                  })}
                </tr>
              );
            })}
          </tbody>
        </table>
      </div>
    </div>
  );
}

function App() {
  const columns = React.useMemo(
    () => [
      {
        Header: "Name",
        columns: [
          {
            Header: "First Name",
            accessor: "firstName"
          },
          {
            Header: "Last Name",
            accessor: "lastName"
          }
        ]
      },
      {
        Header: "Info",
        columns: [
          {
            Header: "Age",
            accessor: "age"
          },
          {
            Header: "Visits",
            accessor: "visits"
          },
          {
            Header: "Status",
            accessor: "status"
          },
          {
            Header: "Profile Progress",
            accessor: "progress"
          }
        ]
      }
    ],
    []
  );

  const data = React.useMemo(() => makeData(2000), []);

  return (
    <Styles>
      <Table columns={columns} data={data} />
    </Styles>
  );
}

export default App;

Activity

  1. tannerlinsley commented on May 9, 2020

    @tannerlinsley
    Member
    • You can't virtualize <table> elements. You'll need to use divs and one of the flex/absolute/block layout plugins from react-table
    • Your headers should not be inside your virtualizer
    • To really achieve what you're talking about requires 2 virtualizers placed at differing layers. 1 vertical virtualizer for the rows, and another horizontal virtualizer for the entire table. This can get quite complex, and there are tons of edge cases, like handling header groupings in separate virtualizers, always showings scrollbars (which is extremely difficult). All reasons that I am building React Table Pro (working title) that will do all of this automatically.
  2. deadcoder0904 commented on May 9, 2020

    @deadcoder0904
    Author

    You can't virtualize elements

    What do you mean by elements here?

    To really achieve what you're talking about requires 2 virtualizers placed at differing layers

    But isn't this a virtualization library? So what do I need to use for virtualizing tables with tons of data (10k+ rows)? Should I use react-window instead?

  3. tannerlinsley commented on May 9, 2020

    @tannerlinsley
    Member

    Sorry I’ve updated my original comment. For some reason I forgot the back ticks around <table> so it just skipped over the code snippet lol

  4. tannerlinsley commented on May 9, 2020

    @tannerlinsley
    Member

    Also, you misunderstood my second point. You can easily virtualize a grid with this library. There are plenty of examples showing that. However, Getting the correct markup to virtualize a complex grid with persistently visible scroll bars and fixed headers is independent of what library you use. If you used React Window, you would still have the same challenges in that scenario.

  5. deadcoder0904 commented on May 9, 2020

    @deadcoder0904
    Author

    Thanks @tannerlinsley

    I understood I need to put the virtualized code in tbody somewhere. Mind answering 1 of my earlier questions 👇

    But I'm confused what to put instead of rowVirtualizer.virtualItems when using .map() inside tbody?

    Also, a simple example in the examples/ folder would be more than helpful I think. I mean one with react-table :)

  6. tannerlinsley commented on May 9, 2020

    @tannerlinsley
    Member

    I will work on an example. Again I need to reiterate, you can’t virtualize a tbody element and you can wrap it in anything that will allow you to. You have to use divs.

  7. deadcoder0904 commented on May 9, 2020

    @deadcoder0904
    Author

    Yes I meant putting the divs inside tbody like:

    <tbody>
      <div ref={parentRef}>
       <div>
           {/*
             this one with the styles.
             the demo example has 2 divs so this will be the inner one
             & then anything in there would be the virtualized one
             or i'm confused if it needs another component.
             i guess i'll wait for an example 😂
           */}
       </div>
      </div>
    </tbody>
  8. tannerlinsley commented on May 9, 2020

    @tannerlinsley
    Member

    Yes conceptually that is how you do it. But that’s invalid html. You can’t have divs as the child of tbody and unless you style the divs as display table or the tbody as a div, that won’t really work either way. All divs. That is the way.

  9. eirikaho commented on May 25, 2020

    @eirikaho
    • React Table Pro

    ETA on this? 👼

  10. piecyk commented on Feb 16, 2021

    @piecyk
    Collaborator

    @tannerlinsley what if we would use pseudo elements on tbody and control them via css custom properties 🤔

    https://codesandbox.io/s/poc-react-virtual-table-jyz0m

  11. jonahallibone commented on Oct 28, 2021

    @jonahallibone
    Contributor

    @piecyk your code works great for me -- except I have no idea what it's doing. Can you explain?

  12. tannerlinsley commented on Jan 20, 2022

    @tannerlinsley
    Member

    This is genius. I saw something similar with other table libraries. Let's make sure this pattern is well-known and supported from here on out.

  13. 2 remaining items

  14. tannerlinsley commented on Feb 25, 2022

    @tannerlinsley
    Member
  15. chrisfernandes102 commented on Feb 25, 2022

    @chrisfernandes102

    @tannerlinsley I am preparing rows that are rendering but I guess my issue is that there seem to be some performance issues with my rows showing in the table using react virtual. The code very much follows what @piecyk did in his awesome example. It almost seems as if it is not rendering the rows fast enough in the dom.

  16. chrisfernandes102 commented on Feb 25, 2022

    @chrisfernandes102

    @tannerlinsley for reference, this is what my table component looks like:

    /** Displays a Table from data and columns */
    export const Table: React.FC<ITableProps> = ({
      h = '100%',
      w = '100%',
      id,
      data,
      columns,
      onColumnChange,
      onCellChange,
    }) => {
      const {
        getTableProps,
        getTableBodyProps,
        headerGroups,
        rows: baseRows,
        prepareRow,
      } = useTable({
        columns,
        data,
      })
      const tableBodyRef = useRef<HTMLDivElement>(null)
      const rowVirtualizer = useVirtual({
        size: baseRows.length,
        parentRef: tableBodyRef,
        estimateSize: useCallback(() => 35, []),
        overscan: 5,
      })
      const tableBodyHeight = rowVirtualizer.totalSize
      const virtualRows = rowVirtualizer.virtualItems
      const paddingTop = virtualRows.length > 0 ? virtualRows[0].start : 0
      const paddingBottom = virtualRows.length > 0 ? tableBodyHeight - virtualRows[virtualRows.length - 1].end : 0
    
      // Default styling that must be applied to the table for virtualization to work.
      const tableStyling = {
        sx: {
          borderCollapse: 'separate',
          borderSpacing: '0',
          height: 'full',
          tableLayout: 'fixed',
          isolation: 'isolate',
        },
        _before: {
          display: 'block',
          paddingTop: `${paddingTop}px`,
          content: '""',
        },
        _after: {
          display: 'block',
          paddingBottom: `${paddingBottom}px`,
          content: '""',
        },
      }
      return (
        <TableScrollbars ref={tableBodyRef} h={h} w={w}>
          <Box as="table" {...getTableProps()} {...tableStyling}>
            <thead>
              <DraggableHeader
                originalColumns={columns}
                id={id}
                headerGroups={headerGroups}
                onColumnChange={onColumnChange}
              />
            </thead>
            <Box as="tbody" {...getTableBodyProps()}>
              {virtualRows.map((virtualRow) => {
                const rowIndex = virtualRow.index
                const row = baseRows[rowIndex]
                prepareRow(row)
                return (
                  <BodyRow index={rowIndex} key={`${id}-row-body-${rowIndex}`} row={row}>
                    {row.cells.map((cell: ITableCell, cellIndex: number) => (
                      <BodyCell
                        key={`${id}-row-body-${rowIndex}-${cellIndex}-${cell.column.isFrozen}`}
                        {...omit(cell.getCellProps(), ['key'])}
                        className="body-cell"
                        cell={cell}
                        isHovered={false}
                        onCellChange={onCellChange}
                      />
                    ))}
                  </BodyRow>
                )
              })}
            </Box>
            <tbody style={{ height: '100%' }}>
              {headerGroups.map((headerGroup, rowIndex) => (
                <SpacingRow {...headerGroup.getHeaderGroupProps()} key={`${id}-row-spacer-${rowIndex}`}>
                  {headerGroup.headers.map((column: HeaderGroup<IRow>, cellIndex) => (
                    <SpacingCell column={column} key={`${id}-row-spacer-${rowIndex}-${cellIndex}-${column.isFrozen}`} />
                  ))}
                </SpacingRow>
              ))}
            </tbody>
            <tfoot>
              {headerGroups.map((headerGroup, rowIndex) => (
                <FooterRow
                  {...headerGroup.getHeaderGroupProps()}
                  headers={headerGroup.headers}
                  key={`${id}-row-footer-${rowIndex}`}
                  numberOfRows={data?.length}
                >
                  {headerGroup.headers
                    .filter((column: HeaderGroup<IRow>) => !column.isFrozen)
                    .map((column: HeaderGroup<IRow>, cellIndex) => (
                      <FooterCell column={column} key={`${id}-row-footer-${rowIndex}-${cellIndex}-${column.isFrozen}`} />
                    ))}
                </FooterRow>
              ))}
            </tfoot>
          </Box>
        </TableScrollbars>
      )
    }
  17. piecyk commented on Feb 25, 2022

    @piecyk
    Collaborator

    @chrisfernandes102 please create codesandbox with the issue, then we can discuss more.

  18. piecyk commented on Feb 25, 2022

    @piecyk
    Collaborator

    Another approach would be include extra rows rather pseudo elements on tbody, also fyi when having a sticky header remember to subtract its height.

    https://codesandbox.io/s/poc-react-virtual-table-forked-p08p9h?file=/src/index.js

  19. chrisfernandes102 commented on Feb 25, 2022

    @chrisfernandes102

    @piecyk @tannerlinsley
    The code I have is probably a bit too complex (and large) to throw into a code sandbox. The overall question I have is how do I allow the scroll to smoothly show data in the DOM, right now it grabs the virtual rows and the experience looks so laggy.

  20. piecyk commented on Feb 25, 2022

    @piecyk
    Collaborator

    Hard to say, first you need to identify what is slow? Nothing new, but checkout points here #74 (comment)

    Also pin v2.8.2 version, in 2.9 we introduce some performance regression to re-render on every scroll.

  21. a9a4k commented on Mar 31, 2022

    @a9a4k

    Made an example by combining @piecyk two approaches https://codesandbox.io/s/poc-react-virtual-table-forked-857kdi?file=/src/index.js

    In a nutshell used dumb trs for adding space

  22. KevinVandy commented on Jun 6, 2022

    @KevinVandy
    Member

    Made an example by combining @piecyk two approaches https://codesandbox.io/s/poc-react-virtual-table-forked-857kdi?file=/src/index.js

    In a nutshell used dumb trs for adding space

    Wow, this works surprisingly well for how simple it is

  23. KevinVandy commented on Jun 9, 2022

    @KevinVandy
    Member

    Made an example by combining @piecyk two approaches https://codesandbox.io/s/poc-react-virtual-table-forked-857kdi?file=/src/index.js

    In a nutshell used dumb trs for adding space

    I tried a similar approach with dumb <td>s to get column virtualization to work, but it didn't really work. Anyone else try anything similar?

  24. wuarmin commented on Jul 15, 2022

    @wuarmin

    Hey @tannerlinsley,
    thank you for your great open source work!
    Please checkout the following virtualization demo of tbody https://html-next.github.io/vertical-collection/#/examples/dbmon

    vertical-collection is an Ember.js project and they managed to virtualize a table with table-markup. Since I switched to react, I have been searching for a similar possibility in react. For now without success. Would something be possible with this project. I am very interested in finding a solution. Sometimes it's not possible to switch to divs. If you think it could be possible (maybe some insights about the difficulties), then I will invest time to make it working.

    I already posted there a question, maybe you are interested
    html-next/vertical-collection#355

    Thank you
    Best regards

  25. tannerlinsley commented on Jul 15, 2022

    @tannerlinsley
    Member
  26. wuarmin commented on Jul 15, 2022

    @wuarmin

    Wow!!! That's amazing! Thank you. I will definitely use it. That sweetens the quarantine for me 😭
    Best regards

  27. bitweedx86 commented on Nov 29, 2022

    @bitweedx86

    We have one now! https://tanstack.com/table/v8/docs/examples/react/virtualized-rows Tanner Linsley
    …
    On Jul 15, 2022, 3:35 AM -0600, Armin @.>, wrote: Hey @tannerlinsley, thank you for your great open source work! Please checkout the following virtualization demo of tbody https://html-next.github.io/vertical-collection/#/examples/dbmon vertical-collection is an Ember.js project and they managed to virtualize a table with table-markup. Since I switched to react, I have been searching for a similar possibility in react. For now without success. Would something be possible with this project. I am very interested in finding a solution. Sometimes it's not possible to switch to divs. Best regards — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.>

    Hi, this example is using react-virtual library. How about @tanstack/react-virtual?

  28. manisha-planetrics commented on May 9, 2023

    @manisha-planetrics

    We have one now! https://tanstack.com/table/v8/docs/examples/react/virtualized-rows Tanner Linsley
    …
    On Jul 15, 2022, 3:35 AM -0600, Armin @.>, wrote: Hey @tannerlinsley, thank you for your great open source work! Please checkout the following virtualization demo of tbody https://html-next.github.io/vertical-collection/#/examples/dbmon vertical-collection is an Ember.js project and they managed to virtualize a table with table-markup. Since I switched to react, I have been searching for a similar possibility in react. For now without success. Would something be possible with this project. I am very interested in finding a solution. Sometimes it's not possible to switch to divs. Best regards — Reply to this email directly, view it on GitHub, or unsubscribe. You are receiving this because you were mentioned.Message ID: @.>

    @tannerlinsley If I'm using @tanstack/react-virtual in this example, the virtualization is not being handled properly? Why so?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions