'How to pass row data to a child component created into a format function

I have a react-bootstrap table with column defined like this:

  const columns = [
    ...
    {
      dataField: "d",
      text: "Actions",
      formatter: actionFormatter,
      headerAlign: "center",
    },
  ];

The formatter function looks like this:

  const actionFormatter = (cell, row) => {
    return (
      <div className="action-cell text-center">
        <Tooltip title="View file">
          <span className="svg-icon svg-icon-xl" onClick={setSelectRow}>
            <SVG
              src={toAbsoluteUrl("/media/svg/icons/General/Binocular.svg")}
            />
          </span>
        </Tooltip>

        <Tooltip title="Download file">
          <span className="svg-icon svg-icon-xl">
            {/* Rename file on download */}
            <SVG
              src={toAbsoluteUrl("/media/svg/icons/Files/Download.svg")}
              onClick={setSelectRow}
            />
          </span>
        </Tooltip>
      </div>
    );
  };

The first part is used to show some content in an other component. I needed to get data to the parent component. But the problem is that the formatter is created once and only once at the component creation, it has no idea about any event or changes happening to row. I already struggled to do this but finished by getting it done by doing something like this:

  const rowEvents = {
    onClick: (e, row, rowIndex) => {
      setSelectedRow(row);
      setSelectedRowIsReady(true);
    },
  };

So, this method allow me to get the selected row by a click event on the row,

then,

  const setSelectRow = useCallback(() => {
    if (selectedRowIsReady) {
      props.onSelectSetting(selectedRow);
    }
  }, [props, selectedRow, selectedRowIsReady]);

  useEffect(() => {
    setSelectRow();
  }, [setSelectRow]);

the setSelectRow function allow me to send my row to the parent components, I had to put it in a useEffect hook because the onClick function was called before the rowEvents.

So... not very clean but at least working as expected.

Now, what I want do to is replace my formatter to look like this:

  <div className="action-cell text-center">
    <Tooltip title="View file">
      <span className="svg-icon svg-icon-xl" onClick={setSelectRow}>
        <SVG
          src={toAbsoluteUrl("/media/svg/icons/General/Binocular.svg")}
        />
      </span>
    </Tooltip>
    <SystemFileDownload row={selectedRow} />
  </div>

However I do not know how to get my row into my child component because, when onClick is called, the state isn't already set. I thought about setting my selected row into my redux store but it feels kind of overkill for what I am trying to achieve.
How could I simply pass my current row to my child component ?

I tried to add my state to formatExtraData like this:

{
  dataField: "d",
  text: "Actions",
  formatter: actionFormatter,
  headerAlign: "center",
  formatExtraData: selectedRow,
},

and then use selectedRow onto my props, but my child component got the row that was click on the previous click and not on the current click.

So I tried to set my row into my redux. Obviously I got the same problem, the onclick function is triggered before the rowEvents function.

So I added this to my child component:

  useEffect(
    (
      downloadFileHandler = () => {
        console.log("Download");
        // Will get the data from the API
        const fake_data = '{"hello": "no"}';
        console.log(selectedConf);
        console.log(systemName);
        const fileName = ``;
      }
    ) => {
      if (firstUpdate.current) {
        firstUpdate.current = false;
        return;
      }
      downloadFileHandler();
    },
    [selectedConf]
  );

Where I get selectedConf from my redux store, and it is set on click on a row.
But then, when I click on my download button, the useEffect hook get triggered as many time as my child component exists on the page, even if it's not part of the BootstrapTable I am working on, so not good, I do not know how to avoid this.

I feel like I am missing something obvious because my use case is like very common and simple and I have to use hooks and redux to achieve it instead of a simple props and it isn't even working.



Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source