nextui: [BUG] - When using Table, "type.getCollectionNode is not a function" error

Describe the bug

When using the Table components, I get an error in the console from inside NextUI’s code :

image

I tried using the Table component two different ways :

  • First, by using the render function as indicated by the documentation :
<Table.Header columns={counterTableColumns}>
  {
    (pColumn: CounterTableColumn) => {
      return (
        <Table.Column
          key={pColumn.uid}
          hideHeader={pColumn.hideHeader}
          align={pColumn.align ?? 'start'}
        >
          {pColumn.name}
        </Table.Column>
      );
    }
  }
</Table.Header>
  • Then by using the Array.map method :
<Table.Header>
  {
    counterTableColumns.map(
      (pColumn: CounterTableColumn) => {
        return (
          <Table.Column
            key={pColumn.uid}
            hideHeader={pColumn.hideHeader}
            align={pColumn.align ?? 'start'}
          >
            {pColumn.name}
          </Table.Column>
        );
      },
    )
  }
</Table.Header>

In both cases, no ESLint or TypeScript errors are displayed in my IDE nor in my terminal.

These test were also made with the Table.Body section of the table.

I am using :

  • Windows 10
  • "@nextui-org/react": "^1.0.0-beta.10"
  • "react": "^18.2.0"
  • "react-scripts": "5.0.1"
  • "typescript": "4.4.2"

Your Example Website or App

No response

Steps to Reproduce the Bug or Issue

CountersList.tsx :

/* Type declarations ----------------------------------- */
interface CounterTableColumn {
  name: string;
  uid: string;
  hideHeader?: boolean;
  align?: 'center' | 'start' | 'end';
}

/* Internal variables & constants ---------------------- */
const counterTableColumns: CounterTableColumn[] = [
  { name: 'Nom', uid: 'name' },
  { name: 'Description', uid: 'role' },
  { name: 'Actions', uid: 'actions', hideHeader: true },
];

const userCounters: CounterLightFragment[] = [
  {
    __typename: 'Counter',
    id: '1',
    name: 'Test1',
    description: 'Une description',
    avatarHash: null,
    avatarContentType: null,
  },
];

/* CountersList component prop types ------------------- */
interface CountersListProps {}

/* CountersList component ------------------------------ */
const CountersList: React.FC<CountersListProps> = () => {
  const userID = useReactiveVar(userIDReactiveVar);

  // const {
  //   data: userData,
  //   loading: loadingUserData,
  //   error: errorUserData,
  // } = useGetUserQuery(
  //   {
  //     variables: {
  //       // eslint-disable-next-line @typescript-eslint/no-non-null-assertion
  //       id: userID!,
  //     },
  //     skip: userID === null,
  //   },
  // );

  // const userCounters: CounterLightFragment[] = userData?.getUser?.counters?.map(
  //   (pVal: UserCounterOwnershipFragment) => {
  //     return pVal.counter;
  //   },
  // ) ?? [];

  console.log(`[DEBUG] <CountersList> userCounters :`, userCounters);

  return (
    <Table bordered>
      <Table.Header>
        {
          counterTableColumns.map(
            (pColumn: CounterTableColumn) => {
              return (
                <Table.Column
                  key={pColumn.uid}
                  hideHeader={pColumn.hideHeader}
                  align={pColumn.align ?? 'start'}
                >
                  {pColumn.name}
                </Table.Column>
              );
            },
          )
        }
      </Table.Header>
      {/* <Table.Header columns={counterTableColumns}>
        {
          (pColumn: CounterTableColumn) => {
            return (
              <Table.Column
                key={pColumn.uid}
                hideHeader={pColumn.hideHeader}
                align={pColumn.align ?? 'start'}
              >
                {pColumn.name}
              </Table.Column>
            );
          }
        }
      </Table.Header> */}
      <Table.Body>
        {
          userCounters.map(
            (pCounter: CounterLightFragment) => {
              return (
                <CountersListItem
                  key={`${pCounter.id}-${pCounter.name}`}
                  counter={pCounter}
                />
              );
            },
          )
        }
      </Table.Body>
      {/* <Table.Body items={userCounters}>
        {
          (pCounter: CounterLightFragment) => {
            return (
              <CountersListItem
                key={`${pCounter.id}-${pCounter.name}`}
                counter={pCounter}
              />
            );
          }
        }
      </Table.Body> */}
      <Table.Pagination
        shadow
        noMargin
        align="center"
        rowsPerPage={10}
        onPageChange={(page) => console.log({ page })}
      />
    </Table>
  );
};

/* Export CountersList component ----------------------- */
export default CountersList;

CountersListItem.tsx :

/* CountersListItem component prop types --------------- */
interface CountersListItemProps {
  counter: CounterLightFragment;
}

/* CountersListItem component -------------------------- */
const CountersListItem: React.FC<CountersListItemProps> = ({ counter }) => {
  return (
    <Table.Row key={counter.id}>
      <Table.Cell>
        <User
          src={counter.avatarHash ?? undefined}
          name={counter.name}
        >
          {counter.description}
        </User>
      </Table.Cell>
      <Table.Cell>
        {JSON.stringify(counter)}
      </Table.Cell>
      <Table.Cell>
        Actions
      </Table.Cell>
    </Table.Row>
  );
};

/* Export CountersListItem component ------------------- */
export default CountersListItem;

Expected behavior

Render the table correctly

Screenshots or Videos

image

Operating System Version

Windows 10

Browser

Chrome

About this issue

  • Original URL
  • State: closed
  • Created 2 years ago
  • Reactions: 1
  • Comments: 22 (2 by maintainers)

Most upvoted comments

Any reason for this limitation? I don’t see a reason why we couldn’t just pass the TableRow a custom as prop or something like that. Main reason for this is you cannot integrate NextUI tables with any other library that has container elements. For example: react-beautiful-dnd basically works out of the box with every UI library I’ve tried before aside from NextUI

Also curious how the builder works, if it looks for a tr element within the table body and then find the cells in that, what does allowing container elements do that’s so bad? I think all that would need to happen in that case is changing how the builder selects the elements, instead of looking for direct child elements, select all the tr elements only for example

This behavior is non sense.

We can not do something like this, which would make sense if you need to do some processing per row, or event if you just want to encapsulate the row logic…

 const MyRow = ({item, ...tableRowProps} : {item: FooItemType}) => {
    const status = useFetchItemStatus(item)
    return (
      <TableRow {...tableRowProps} key={`example-${item.id}`}>
          <TableCell>{item.foo}</TableCell>
          <TableCell>{status || "loading satus"}</TableCell>
      </TableRow>
    )
 }

.... 
// Then in the parent component you would do
...

<Table aria-label="Example table">
    <TableHeader>
        <TableColumn>Col A</TableColumn>
        <TableColumn>Col B</TableColumn>
    </TableHeader>
    <TableBody>
        {data.map(item => (
            <MyRow item={item} />
        ))}
    </TableBody>
</Table>

So honestly I don’t understand the logic of not allowing this, this issue should remain opened imo since no solution was provided.

So is there currently no way to breakout NextUI Table components into separate components? If so that is pretty disappointing considering the benefits you can gain from splitting things into server and client components.

also got this issue with: DropdownItem

Same issue with the <Listbox> component… Considering the entire point of React is to breakdown complex UI into smaller components, this is a bit of a weird and unexpected behaviour.

Yeah tried using the table again and got this same error. Unfortunately, it makes this table component fairly useless. Especially if you need to manage state for individual rows. There are refactoring work arounds but most of them are anti-patterns for react. Love this library otherwise.

@lazharichir MenuItem also

What makes this quite cumbersome is that say if I have AccordionItems that do not render due to some logic inside them, now all that logic needs to be pulled up into the parent component so the AccordionItems can be conditionally removed. It basically promotes leaky abstractions. 😞

I did try to just assign getCollectionNode to my custom components. It does stop complaining but it doesn’t actually render the custom component properly.