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

Simple Content Tab with React and Tailwind

The foundation for a tab system begins with a state hook, triggered by clicks on tab elements. View the code snippet and use the playground to see it in action.

May 28, 2022

How to implement HTTPS in local development for React, Angular, and Node

Using an SSL certificate can be beneficial in local development, especially when communicating with external services.

Anonymous
Jan 11, 2023

Use a Class Map to Set Dynamic Styles

Components often need to use styling based on property combinations. There are a number of ways to solve this, but only one I’ve found to be the cleanest.

Feb 03, 2023