Make the rows of Semantic UI's Table clickable

Viewed 795

The table is the one from Semantic UI.

One way to make the the rows clickable is to add Link from react-router-dom.

Like here:

import React from 'react';
import { Table } from 'semantic-ui-react';
import { Link } from 'react-router-dom'; // used for Link

export default class GenericTable extends React.PureComponent {
  constructor(props) {
    super(props);
  }

  render() {
    const {
      headers,
      emptyFirstHeader,
      rows,
      id,
      entityName,
      idList,
    } = this.props;

    return (
      <Table id={id}>
        <Table.Header>
          <Table.Row>
            <Table.HeaderCell />
          </Table.Row>
        </Table.Header>

        <Table.Body>
          {rows.map((row, rowIndex) => (
            <Table.Row
              key={idList && idList[rowIndex]}
              as={Link} // this line makes the row clickable but also adds the errors
              to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`}> // the location of the redirect
              {row.cells.map((cell, cellIndex) => {
                if (cell === undefined) {
                  return null;
                }
                return (
                  <Table.Cell
                    key={idList && `${idList[rowIndex]} ${headers[cellIndex]}`}>
                    {cell}
                  </Table.Cell>
                );
              })}
            </Table.Row>
          ))}
        </Table.Body>
      </Table>
    );
  }
}

The problem is that it returns an error in Developer tools:

index.js:1 Warning: validateDOMNesting(...): <td> cannot appear as a child of <a>.
in td (created by TableCell)
in TableCell (at GenericTable.js:193)
in a (created by LinkAnchor)
in LinkAnchor (created by Context.Consumer)
in Link (created by TableRow)
in TableRow (at GenericTable.js:143)
in tbody (created by TableBody)
in TableBody (at GenericTable.js:141)
in table (created by Table)
in Table (at GenericTable.js:105)

Tried to solve it by replacing as={Link} with an onClick and connecting it to a separate function. That error message disappears now but when a row is clicked it does not redirect to the desired page.

I added a console.log inside the function to check if it is called and it is.

This is the code:

  navigateTo = (entityName, idList, rows, row) => {
    console.log('click: ', entityName, row); // the log works
    return (
      <Link to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`} />
    );
  };

...
            <Table.Body>
              {rows.map((row, rowIndex) => (
                <Table.Row
                  key={idList && idList[rowIndex]}
                  onClick={() => {
                    this.navigateTo(entityName, idList, rows, row);
                  }}
                  to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`}> // the location of the redirect
                  {row.cells.map((cell, cellIndex) => {
                    if (cell === undefined) {
                      return null;
                    }
                    return (
                      <Table.Cell
                        key={idList && `${idList[rowIndex]} ${headers[cellIndex]}`}>
                        {cell}
                      </Table.Cell>
                    );
                  })}
                </Table.Row>
              ))}
            </Table.Body>

Any ideas why is not working and how should it be modified?

2 Answers

On your usage of <Table.Row as={Link} />, the console prints those errors (warnings to be more precise) because, what this does is it renders the tr element as a, so this is what the DOM would look like in this approach:

enter image description here

<td> cannot appear as a child of <a>

and

<a> cannot appear as a child of <tbody>

It is simply a warning and should not interfere with the rest of the script executions, so the redirection still works as you have indicated. But, it logs warnings, and as you have described this also causes styling issues - because now the tr has transformed into an a tag.

To salvage this Link implementation, you could have these links as child to the td elements.

<Table.Body>
  {rows.map((row, rowIndex) => (
    <Table.Row
      key={idList && idList[rowIndex]}
    >
      {row.cells.map((cell, cellIndex) => {
        if (cell === undefined) {
          return null;
        }
        return (
          <Table.Cell
            key={idList && `${idList[rowIndex]} ${headers[cellIndex]}`}>
            <Link to={entityName && `/${entityName}/${idList[rows.indexOf(row)]}`}>{cell}</Link>
          </Table.Cell>
        );
      })}
    </Table.Row>
  ))}
</Table.Body>

This will get rid of the console warnings because now, the a tags are now a child of td which is valid HTML structure. Note that you may have to do some CSS refactoring for the entire row to be clickable. You need to expand each a tag to fit the height of the tr and the width of its parent td.


A more ideal solution to this is just to leverage the onClick prop of Table.Row like what you have attempted. In your attempt however, this does not really redirect the user because it simply returns some JSX when it is clicked - there is not really any routing that occured.

To salvage this onClick implementation, you can use withRouter higher-order component.

import { withRouter } from "react-router-dom";

class GenericTable extends React.PureComponent { ... }

export default withRouter(props => <GenericTable {...props} />);

We can use the same onClick prop you have but we will refactor some code on navigateTo method

navigateTo = (entityName, idList, rows, row) => {
  console.log('click: ', entityName, row); // the log works
  entityName && this.props.history.push(`/${entityName}/${idList[rows.indexOf(row)]}`);
};

<Table.Row
  key={idList && idList[rowIndex]}
  onClick={() => {
    this.navigateTo(entityName, idList, rows, row);
  }}
  ...

(This assumes that you are using client-side-routing. If not, just use entityName && window.location.href = "http://linkToRedirectTo.com")

Something similar to the answer at the top: [https://codesandbox.io/s/determined-bash-izqsi?file=/src/example.jsx][1]

Related