changing background-color of a <tr> in react with useRef

Viewed 1300

I created a plain dynamic table in react (not using react-table). I want to change the background color of a clicked row. I added ref to table: but I'm not getting access to children ().

How can I change the style of a clicked row by id in a table in react?

import React, { useEffect, useRef } from "react";
import "./TableBox.css";

const TableBox = ({ table_data }) => {
  const myTable = useRef(null);

  useEffect(() => {
    console.log("myTable", myTable.current);
  });

  const handlerRowClicked = (event) => {
    // console.log('clicked')
    // myTable.current.backgroundColor='blue'
  };

  return (
    <div>
      <table ref={myTable}>
        <thead>
          <tr>
            {Object.keys(table_data[0]).map((title) => (
              <th key={title}>{title}</th>
            ))}
          </tr>
        </thead>
        <tbody>
          {table_data.map((row, index) => (
            <tr key={index} id={index} onClick={handlerRowClicked}>
              {Object.keys(row).map((key) => (
                <td key={row[key]}>{row[key]}</td>
              ))}
            </tr>
          ))}
        </tbody>
      </table>
    </div>
  );
};

export default TableBox;
2 Answers

Using refs for this purpose is most likely an over kill. Instead, I'd suggest you to useState, and attach either a className or style.

For instance:

import React, { useEffect, useRef } from 'react'
import './TableBox.css'

const TableBox = ({ table_data }) => {
    const [selectedRow, setSelectedRow] = useState(-1);
    ...

    const handlerRowClicked = useCallback((event) => {
         // if you insist to use `id`
         const { id } = event.currentTarget;
         setSelectedRow(id);
     }, [])
...
<table>
 ...
   <tr onClick={handlerRowClicked} className={selectedRow === index ? 'selected: ''}>

There are some better practices how to handle className but this shows the idea.

The simplest solution that you can do is to save index of clicked row in state and use that as condition when looping trough table_data.

const TableBox = ({ table_data }) => {
  const myTable = useRef(null);

  const [rowIndexClicked, setRowIndexClicked] = useState(null);

  useEffect(() => {
    console.log("myTable", myTable.current);
  });

  const handlerRowClicked = (rowIndex) => (event) => {
    if (rowIndexClicked !== rowIndex) {
      // handle if user clicks again the same row
      setRowIndexClicked(rowIndex);
    } else {
      setRowIndexClicked(null); // set clicked row to null if same row is selected
    }
  };

  return (
    ...
          {table_data.map((row, index) => (
            <tr
              key={index}
              id={index}
              className={rowIndexClicked === index ? "clicked-class" : ""}
              onClick={handlerRowClicked(index)}
            >
              {Object.keys(row).map((key) => (
                <td key={row[key]}>{row[key]}</td>
              ))}
            </tr>
          ))}
    ...
  );
};

Using useRef wont give you any edge. You can store clicked element in it, but there is not reason not to go the simplest way.

SOLUTION 2

If you still want to take id from the clicked element itself you can just use event that you inherently get from click event:

const TableBox = ({ table_data }) => {
  const myTable = useRef(null);

  const [rowIndexClicked, setRowIndexClicked] = useState(null);

  useEffect(() => {
    console.log("myTable", myTable.current);
  });

  const handlerRowClicked = (event) => {
    const { id } = event.currentTarget;
    if (rowIndexClicked !== id) {
      // handle if user clicks again the same row
      setRowIndexClicked(id);
    } else {
      setRowIndexClicked(null); // set clicked row to null if same row is selected
    }
  };

  return (
    ...
          {table_data.map((row, index) => (
            <tr
              key={index}
              id={index}
              className={rowIndexClicked === index ? "clicked-class" : ""}
              onClick={handlerRowClicked}
            >
              {Object.keys(row).map((key) => (
                <td key={row[key]}>{row[key]}</td>
              ))}
            </tr>
          ))}
    ...
  );
};
Related