Home

Storing Multiple Elements in a Single Ref in React

In some cases, you may want multiple references in a component of unknown quantity. Here’s how you can make it work.

A typical use of the useRef hook is to be able to access the HTML element directly. This is the example from the React docs:

function TextInputWithFocusButton() {
const inputEl = useRef(null);
const onButtonClick = () => {
// `current` points to the mounted text input element
inputEl.current.focus();
};
return (
<>
<input ref={inputEl} type="text" />
<button onClick={onButtonClick}>Focus the input</button>
</>
);
}

This unlocks the ability to access native properties and call native functions on that element.

Accounting for Multiple (Unknown) Refs

I often run into a scenario in which I want direct access to elements with a component (like the example above), but I don’t know how many components there will be.

Consider if we had a similar component, but rather than focusing a single text input, the button would tab through a series inputs of unknown quantity. In that case, we might track the active input with a state, and then increment the index with each button click.

Because I don’t know how many inputs there will be, I can’t use useRef directly on each one. The workaround is to store the ref as an array ...

const inputEls = useRef([]);

... and then pass a function when applying the reference.

// An example where `idx` is a known index value
<input ref={(el) => (inputEls.current[idx] = el)} type="text" />

In context, that might look something like this:

import React, { useRef, useState } from "react";

export function Component(props) {
const [nextIdx, setNextIdx] = useState(0);
const inputEls = useRef([]);

const onButtonClick = (idx) => {
inputEls.current[nextIdx].focus();
// Find the new next index.
setNextIdx(nextIdx + 1 >= props.inputCount ? 0 : nextIdx + 1);
};

return (
<>
{Array(props.inputCount)
.fill()
.map((_, idx) => (
<input
ref={(el) => (inputEls.current[idx] = el)}
type="text"
style={{ display: "block", marginBottom: "0.5rem" }}
/>

))}
<button onClick={onButtonClick}>Tab through inputs</button>
</>
);
}

That leads to this behavior:

Playground

Here’s a playground with this code so you can see it in action.

Let's Connect

Keep Reading

Animated Sliding Tabs with React and Tailwind

Add some flair to a simple tab solution by adding an animated bottom border.

May 29, 2022

How to Add SVG Icons to a React Project

Vector icons have become an essential part of nearly every web project. Stay consistent and organized when adding icons to a React project.

Oct 06, 2022

Difference Between React and Vue.js

React and Vue are two of the most popular component frameworks used in modern website development. Let's break down the differences between these two everyday tools.

Harikrishna Kundariya
Jul 22, 2022