So I have an array of data in and I am generating a list of components with that data. I'd like to have a ref on each generated element to calculate the height. I know how to do it with a Class component, but I would like to do it with React Hooks.
Here is an example explaining what I want to do:
import React, {useState, useCallback} from 'react' const data = [ { text: 'test1' }, { text: 'test2' } ] const Component = () => { const [height, setHeight] = useState(0); const measuredRef = useCallback(node => { if (node !== null) { setHeight(node.getBoundingClientRect().height); } }, []); return ( <div> { data.map((item, index) => <div ref={measuredRef} key={index}> {item.text} </div> ) } </div> ) }
A ref can be created in two ways- by the useRef hook or by the createRef function. useRef: The useRef is a hook that uses the same ref throughout. It saves its value between re-renders in a functional component and doesn't create a new instance of the ref for every re-render.
There are two ways to create refs in React the first is by using React. createRef() method or the useRef() hook. In this article, we will be using the hook. First, we import the useRef hook and use it to create a ref, passing null to it as an argument.
Creating RefsRefs are created using React. createRef() and attached to React elements via the ref attribute. Refs are commonly assigned to an instance property when a component is constructed so they can be referenced throughout the component.
So the reason you shouldn't list a ref in your useEffect dependency array is because it's an indication that you want the effect callback to re-run when a value is changed, but you're not notifying React when that change happens.
Not sure i fully understand your intent, but i think you want something like this:
const { useState, useRef, createRef, useEffect } = React; const data = [ { text: "test1" }, { text: "test2" } ]; const Component = () => { const [heights, setHeights] = useState([]); const elementsRef = useRef(data.map(() => createRef())); useEffect(() => { const nextHeights = elementsRef.current.map( ref => ref.current.getBoundingClientRect().height ); setHeights(nextHeights); }, []); return ( <div> {data.map((item, index) => ( <div ref={elementsRef.current[index]} key={index} className={`item item-${index}`}> {`${item.text} - height(${heights[index]})`} </div> ))} </div> ); }; const rootElement = document.getElementById("root"); ReactDOM.render(<Component />, rootElement);
.item { box-sizing: border-box; display: flex; align-items: center; justify-content: center; border: 1px solid #ccc; } .item-0 { height: 25px; } .item-1 { height: 50px; }
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.6/umd/react.production.min.js"></script> <script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.6/umd/react-dom.production.min.js"></script> <div id="root"/>
I created a tiny npm
package that exposes a React Hook
to handle setting and getting refs dynamically as I often run into the same problem.
npm i use-dynamic-refs
Here's a simple example.
import React, { useEffect } from 'react'; import useDynamicRefs from 'use-dynamic-refs'; const Example = () => { const foo = ['random_id_1', 'random_id_2']; const [getRef, setRef] = useDynamicRefs(); useEffect(() => { // Get ref for specific ID const id = getRef('random_id_1'); console.log(id) }, []) return ( <> {/* Simple set ref. */} <span ref={setRef('random_id_3')}></span> {/* Set refs dynamically in Array.map() */} { foo.map( eachId => ( <div key={eachId} ref={setRef(eachId)}>Hello {eachId}</div>))} </> ) } export default Example;
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With