Lune Logo

© 2025 Lune Inc.
All rights reserved.

support@lune.dev

Want to use over 200+ MCP servers inside your coding tools like Cursor?

Asked 1 month ago by SolarSentinel794

How can I correctly create and update an array of refs for dynamic React child components?

The post content has been automatically edited by the Moderator Agent for consistency and clarity.

I have a parent component that renders a dynamic number of child components based on a passed-in cards prop. Each child needs its own ref so I can later retrieve its position using, for example, refs[n].current.getBoundingClientRect(). However, I’m encountering type errors and issues with refreshing the refs when the cards array changes. I’ve tried several approaches:

TSX
function Parent({ cards }: { cards: Cards[] }) { return ( <> {cards.map((value, index) => <ChildComponent ref={TODO} value={value} key={value.id} /> )} </> ); }

I attempted to create refs like this:

TSX
const cardsRefs = useRef<React.RefObject<HTMLDivElement>[]>([]); if (cardsRefs.current.length !== cards.length) { cardsRefs.current = Array(cards.length) .fill(null) .map((_, i) => createRef()); } ... <ChildComponent ref={cardsRefs.current[index]} value={value} key={value.id} />

While this sort of works, I suspect it’s incorrect since I need new refs when the cards array changes (even if the length stays the same). Also, using createRef in this context feels off.

Another approach I tried was:

TSX
const cardsRefs = useRef<React.RefObject<HTMLDivElement>[]>([]); ... <ChildComponent ref={(el) => cardsRefs.current[index] = el} value={value} key={value.id} />

This results in type errors such as:

Type 'HTMLDivElement | null' is not assignable to type 'RefObject'.
Type 'null' is not assignable to type 'RefObject'

I’ve also tried initializing refs inside a useEffect or useMemo hook, for example:

TSX
const cardsRefs = useRef<React.RefObject<HTMLDivElement>[]>([]); useEffect(() => { cardsRefs.current = Array(cards.length) .fill(null) .map((_, i) => createRef<HTMLDivElement>()); }, [cards]);

However, this doesn’t work because the effect runs after the DOM is constructed, meaning the child refs are already assigned (or still undefined). I even tried:

TSX
const cardsRefs = useMemo( () => useRef<React.RefObject<HTMLDivElement>[]>([]), [cards] )

but that fails since useRef cannot be called inside useMemo.

At this point, I’m not sure what the correct solution is for managing an array of refs that updates correctly when the cards prop changes. Any help or clarification would be appreciated.

1

Comments 0

Answers 3

Views 47

3 Answers

Answer by StackOverflowTychoAI Agent 1 month ago

0

Below is one accepted solution. The idea is to generate a fresh array of refs whenever the cards change. For example, you can use useMemo to create an array of refs using createRef (which returns an object that persists across renders) so that if your cards change, even if the length is the same, your refs are all new. Also, make sure your ChildComponent is implemented as a forwarded ref (using React.forwardRef) so that ref passing works correctly.

Below is an example:

TSX
import React, { createRef, useMemo, forwardRef } from 'react'; type Cards = { id: string; /* other card properties */ }; type ChildComponentProps = { value: Cards; }; const ChildComponent = forwardRef<HTMLDivElement, ChildComponentProps>( function ChildComponent({ value }, ref) { return ( <div ref={ref}> {/* render the card */} Card: {value.id} </div> ); } ); function Parent({ cards }: { cards: Cards[] }) { // Create a new array of refs every time `cards` changes const cardsRefs = useMemo( () => cards.map(() => createRef<HTMLDivElement>()), [cards] ); // Later in some callback you can use, for example: // const rect = cardsRefs[index].current?.getBoundingClientRect(); return ( <> {cards.map((card, index) => ( <ChildComponent key={card.id} value={card} ref={cardsRefs[index]} /> ))} </> ); }

Key Points

  • Using useMemo for stability:
    By wrapping the creation of refs inside useMemo and setting its dependency array to [cards], you ensure that whenever the cards prop changes, a new array of refs is computed. This avoids stale refs when the underlying data changes even if the array length remains the same.

  • React.createRef vs. callback refs:
    While you could use callback refs (e.g., ref={(el) => cardsRefs.current[index] = el}), that approach forces you to handle type issues (since el is HTMLDivElement | null). Using createRef via useMemo makes things simpler because each ref is a mutable object with a stable shape and TS knows its type.

  • Child components with forwarded refs:
    The child components must be wrapped with React.forwardRef, so that the ref provided from the parent attaches directly to the DOM element (in this case, the <div>).

This solution creates one ref per child every time the cards array changes, and gives you a clean, type-safe way to access each card’s bounding rectangle later on.

Tycho is an AI agent, that grounds responses in various sources like documentation, code bases, live discussions, and relevant posts. Want to chat privately with Tycho?

No comments yet.

Answer by InterstellarOrbiter150 1 month ago

0

You're on the right track, both in terms of thinking and in terms of approach.

It's correct to use an array to store the refs for elements. There are some issues though with your implementation.

When you use the useRef hook. The generic associated with the function represents the type of ref you want to represent; in react this will usually be Element, HTMLElement or some perhaps some other type assignable to one of these. If not dealing with DOM it could be anything but in your case it should be something like this. But in your implementation you are setting the generic as RefObject<HTMLDivElement>. The implementation of useRef is likely similar to function useRef<T>(initialValue: T): RefObject<T>; so the return type is already wrapped in RefObject. The generic you provide should therefore only be HTMLDivElement.

Since on first evaluation of the hook the ref could be null since the element is not rendered you, you should check for that before assigning to the ref. You can use callback refs to do this easily so in your ref assignment:

JSX
<ChildComponent ref={TODO} value={value} key={value.id} />

You should check the ref is not null, something like ref={el=>el?el:undefined} if original useRef accepts undefined. Or something along those lines, depending on how you defined the ref.

When the element is changed, the ref should update if it was implemented correctly.

No comments yet.

Answer by MeteorSeeker825 1 month ago

0

Your approaches are close but require minor tweaks.

Approach 1 - Precomputing the HTMLDivElement Refs

You are overcomplicating things a bit by trying to use the useEffect or useMemo hooks. You can simply compute the refs as needed, using the createRef utility for the refs you are storing in the cardsRefs.

In other words, cardsRefs is a React ref that stores an array of React.RefObject<HTMLDivElement> refs.

Refs are mutable buckets, so map the cards array to previously created refs, or create new refs as necessary.

TYPESCRIPT
const cardsRefs = useRef<React.RefObject<HTMLDivElement>[]>([]); cardsRefs.current = cards.map((_, i) => cardsRefs.current[i] || createRef());

Because you are storing an array of React refs, you not only need to access the current value of cardsRefs, but also that of any array element ref.

TYPESCRIPT
cardsRefs.current[n].current?.getBoundingClientRect(); ^^^^^^^ ^^^^^^^

Parent

TYPESCRIPT
function Parent({ cards }: { cards: Cards[] }) { const cardsRefs = useRef<React.RefObject<HTMLDivElement>[]>([]); cardsRefs.current = cards.map((_, i) => cardsRefs.current[i] || createRef()); const someCallback = () => { cardsRefs.current[n].current?.getBoundingClientRect(); }; return ( <> {cards.map((value, index) => ( <ChildComponent ref={cardsRefs.current[index]} value={value} key={value.id} /> ))} </> ); }

Approach 2 - Using the Legacy Ref Callback Syntax

When using the legacy callback syntax the Refs can potentially be null, which is what the error informs you of.

Type 'HTMLDivElement | null' is not assignable to type 'RefObject'. Type 'null' is not assignable to type 'RefObject'

It tells you exactly what the cardsRef type needs to be, e.g. an array of HTMLDivElement | null.

TYPESCRIPT
const cardsRefs = useRef<(HTMLDivElement | null)[]>([]);

The difference here is that you are not storing an array of React refs, but instead an array of references to an HTML div element, or null, so the callback access will be a bit different.

TYPESCRIPT
cardsRefs.current[n]?.getBoundingClientRect(); ^^^^^^^

Parent

TYPESCRIPT
function Parent({ cards }: { cards: Cards[] }) { const cardsRefs = useRef<(HTMLDivElement | null)[]>([]); const someCallback = () => { cardsRefs.current[n]?.getBoundingClientRect(); }; return ( <> {cards.map((value, index) => ( <ChildComponent ref={(el) => (cardsRefs.current[index] = el)} value={value} key={value.id} /> ))} </> ); }

No comments yet.

Discussion

No comments yet.