Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

react-window: How to use actual table tags

I want to use the semantic HTML tags (instead of using divs) to create a table with react-window.

The problem is that List (FixedSizedList) creates two wrappers. The other one is called outerElementType and is also a prop to FixedSizedList with default value div. This means that I can't create the proper table structure, and that all the td ends up in the first column . It looks like neither of these ones can be omitted. How do I get around this?

Current code:

import { FixedSizeList as List } from "react-window";

...

return (

   <table className="CargoListTable">
      <CargoTableHead />
      <List
        height={600}
        itemCount={cargoList.length}
        itemSize={35}
        width={900}
        itemData={cargoList}
        innerElementType="tbody"
      >
        {Row}
      </List>
   </table>
 )

const Row: React.FC<RowProps> = ({ index, style, data }) => {
  const cargo = data[index];
  return (
    <tr
      style={style}
      key={index}
    >
      <td>{cargo.registrationNumber}</td>
      <td>{cargo.pol}</td>
      <td>{cargo.pod}</td>
    </tr>
  );
};
like image 668
hellogoodnight Avatar asked Mar 04 '26 06:03

hellogoodnight


1 Answers

One possible solution to this would be to have the entire table inside the list. We can use a modified version of the sticky-header example from react-window for this.

You can view a working example in this CodeSandbox: https://codesandbox.io/s/wild-dust-jtf42?file=/src/index.js

We will need two simple elements to render StickyRow and Row elements. You can add td elements here.

const Row = ({ index, style }) => (
  <tr className="row" style={style}>
    Row {index}
  </tr>
);

const StickyRow = ({ index, style }) => (
  <tr className="sticky" style={style}>
    <th>Sticky Row {index}</th>
  </tr>
);

We wrap the FixedSizeList in a Context that contains the sticky rows. In this case only the first row would be sticky.

const StickyList = ({ children, stickyIndices, ...rest }) => (
  <StickyListContext.Provider value={{ ItemRenderer: children, stickyIndices }}>
    <List itemData={{ ItemRenderer: children, stickyIndices }} {...rest}>
      {ItemWrapper}
    </List>
  </StickyListContext.Provider>
);

ItemWrapper renders only the non-sticky rows using the method passed in the main render function(ie- {Row}). This takes care of rendering the table data.

const ItemWrapper = ({ data, index, style }) => {
  const { ItemRenderer, stickyIndices } = data;
  if (stickyIndices && stickyIndices.includes(index)) {
    return null;
  }
  return <ItemRenderer index={index} style={style} />;
};

To render the table headers, we need a custom innerElementType.

const innerElementType = forwardRef(({ children, ...rest }, ref) => (
  <StickyListContext.Consumer>
    {({ stickyIndices }) => (
      <table ref={ref} {...rest}>
        {stickyIndices.map(index => (
          <StickyRow
            index={index}
            key={index}
            style={{ top: index * 35, left: 0, width: "100%", height: 35 }}
          />
        ))}

        <tbody>
          {children}
        </tbody>
      </table>
    )}
  </StickyListContext.Consumer>
));

This element is aware of the sticky indices because of the context. And renders the header and the body.

This code could be simplified further if it suites your needs.

like image 65
nipuna777 Avatar answered Mar 05 '26 22:03

nipuna777