0

I am attempting to code a <List> component that is able to print the (x, y) coordinates of each <Row> entry that is added. The problem is that there is an intermediate <RowGroup> component that holds <Row> entries, where I plan on having the <RowGroup> be an abstract type.

I followed this answer regarding innerRef and this answer regarding pushing refs from children components, and I wrote the below code that seems to work.

But it feels kind of hack-ish. Apparently, you must use forwardRef to forward refs, and in this case I am using an innerRef prop and it works? Is it legal to push refs from the child component, into the parent's parent's ref list? I know you aren't supposed to do stuff like having an array of refs, and this feels like a different way to bypass that.

Is there a different way I should structure this code to be more valid? Are there any cases where this code wouldn't work?

import {useRef, useEffect, forwardRef, useImperativeHandle} from 'react';

function App() {
  const rowGroups = [
    {rows: [{text: "A"}, {text: "B"}, {text: "C"}]},
    {rows: [{text: "D"}, {text: "E"}]}
  ]
  return <List rowGroups={rowGroups}></List> // List should be: A B C {spacing} D E
}

const List = ({rowGroups}) => {

  let rowRefs = useRef([]);

  function handleClick() {
    for (let i = 0; i < rowRefs.current.length; i++) {
      const rect = rowRefs.current[i].getBoundingClientRect();
        console.log(rect.left, rect.top); // print coordinates of each row. 
      }
  }
  
  return (
    <div>
    <div>
      {rowGroups.map((rowGroup, _) => (
        <RowGroup key={_} rowGroup={rowGroup} innerRef={rowRefs}/>
      ))}
    </div>
    <button onClick={() => handleClick()}></button>
    </div>
  );
}

const RowGroup = ({rowGroup, innerRef}) => {

  return (
    <div style={{padding: 50}}>
      {rowGroup.rows.map((row, _) => (

        <Row key={_} row={row} innerRef={innerRef}/> 
      ))}
    </div>
  );
};

const Row = ({row, innerRef}) => { 
    return (
        <div ref={(ref) => {innerRef.current.push(ref)}} style={{height: 50, width: 200}}>
            {row.text}
        </div> 
    );
}

export default App;

Thanks.

Snowybluesky
  • 47
  • 2
  • 7
  • This does work indeed, but you will run into problems when the list changes. This is because you're using the `map` index as the key for each row, and secondly, you're not removing the ref when a row gets unmounted. – Chris Dec 20 '22 at 14:39

0 Answers0