How can I use multiple refs for an array of elements with hooks?

devserkan picture devserkan · Feb 11, 2019 · Viewed 51.1k times · Source

As far as I understood I can use refs for a single element like this:

How can I implement this for an array of elements? Obviously not like that: (I knew it even I did not try it:)

const { useRef, useState, useEffect } = React;

const App = () => {
  const elRef = useRef();
  const [elWidth, setElWidth] = useState();

  useEffect(() => {
    setElWidth(elRef.current.offsetWidth);
  }, []);

  return (
    <div>
      {[1, 2, 3].map(el => (
        <div ref={elRef} style={{ width: `${el * 100}px` }}>
          Width is: {elWidth}
        </div>
      ))}
    </div>
  );
};

ReactDOM.render(
  <App />,
  document.getElementById("root")
);
<script src="https://unpkg.com/react@16/umd/react.production.min.js"></script>
<script src="https://unpkg.com/react-dom@16/umd/react-dom.production.min.js"></script>

<div id="root"></div>

I have seen this and hence this. But, I'm still confused about how to implement that suggestion for this simple case.

Answer

Olivier Boiss&#233; picture Olivier Boissé · May 9, 2019

As you cannot use hooks inside loops, here is a solution in order to make it work when the array changes over the time.

I suppose the array comes from the props :

const App = props => {
    const itemsRef = useRef([]);
    // you can access the elements with itemsRef.current[n]

    useEffect(() => {
       itemsRef.current = itemsRef.current.slice(0, props.items.length);
    }, [props.items]);

    return props.items.map((item, i) => (
      <div 
          key={i} 
          ref={el => itemsRef.current[i] = el} 
          style={{ width: `${(i + 1) * 100}px` }}>
        ...
      </div>
    ));
}