How to Use Multiple Refs for an Array of Elements with Hooks

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

A ref is initially just { current: null } object. useRef keeps the reference to this object between component renders. current value is primarily intended for component refs but can hold anything.

There should be an array of refs at some point. In case the array length may vary between renders, an array should scale accordingly:

const arrLength = arr.length;
const [elRefs, setElRefs] = React.useState([]);

React.useEffect(() => {
// add or remove refs
setElRefs((elRefs) =>
Array(arrLength)
.fill()
.map((_, i) => elRefs[i] || createRef()),
);
}, [arrLength]);

return (
<div>
{arr.map((el, i) => (
<div ref={elRefs[i]} style={...}>
...
</div>
))}
</div>
);

This piece of code can be optimized by unwrapping useEffect and replacing useState with useRef but it should be noted that doing side effects in render function is generally considered a bad practice:

const arrLength = arr.length;
const elRefs = React.useRef([]);

if (elRefs.current.length !== arrLength) {
// add or remove refs
elRefs.current = Array(arrLength)
.fill()
.map((_, i) => elRefs.current[i] || createRef());
}

return (
<div>
{arr.map((el, i) => (
<div ref={elRefs.current[i]} style={...}>
...
</div>
))}
</div>
);

How can I use multiple refs for nested arrays of elements with hooks, or any other way

You can extract the child into a separate component and create the ref there.

const { useRef, useState } = React;

const arr1 = ["A", "B", "C"];
const arr2 = [1, 2, 3];

const Child = ({ childItem }) => {
const liRef = useRef();
const handleClick = () => {
liRef.current.classList.toggle("highlight");
};
return (
<div ref={liRef}>
A Child Element {childItem}{" "}
<button onClick={handleClick}>Highlight</button>
</div>
);
};

const App = () => {
return (
<div>
{arr1.map((parentItem, index) => {
return (
<div key={index}>
<strong>A Parent Element {parentItem}</strong>
{arr2.map((childItem, index2) => {
return <Child childItem={childItem} key={index2}></Child>;
})}
</div>
);
})}
</div>
);
};

const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(
<React.StrictMode>
<App />
</React.StrictMode>
);
.highlight {
background: yellow;
color: red;
}
<script crossorigin src="https://unpkg.com/react@18/umd/react.development.js"></script>
<script crossorigin src="https://unpkg.com/react-dom@18/umd/react-dom.development.js"></script>
<div id="root"></div>

In react, how can I add multiple ref to the element using map?

you don't need to use for loop to push the elements in ref, you already use map in return you can push textarea elements using ref like this way as you can see the below code, I hope this works. thanks

import { useRef } from "react";

export default function RenderPages({storage, setStorage, state, setState}) {
const elRefs = useRef([]);

return (
<>
{renderable ? (
<div className="writing">
{storage[state.currentFolderId][state.currentFileId].content.map((page, index) => (
<div className="textarea">
<textarea
ref={ref => {
elRefs.current[index] = ref
}}
placeholder="write here"
value={page}
id={'page' + index}
onChange={e => onChange(e, index)}
rows={rows}
cols={cols}></textarea>
</div>
))}
</div>
) : (
<></>
)}
</>
);
}

How can I grab multiple mapped elements using useRef?

Best solution: don't use refs you probably don't need them. If you tell us why you think you need refs we can verify if you're right.

Second best, create an array of refs of the same length as your skills list. A ref can only point to one object. What you're doing is placing the same refs on many objects which isn't how refs are designed to work.

const refs = [];
for (let i = 0; i < skills.length; i++) {
refs.push(useRef());
}
return (
<div className='images-container'>
{skills.map((skill, idx) => {
return <div key={idx}>
<img alt={skill.name} ref={refs[idx]} src={skill.url} />
<p>{skill.name}</p>
</div>
})}
</div>
)

How to add two refs in input? reactjs

From frequent try and error, I finally found the solution. The refs can actually use as function and then set two refs in it.

And for the react hook form just pass the event on it to make it work also.

Here's the actual solution:

const [inputRef, setInputRef] = useFocus();

{[...Array(10)].map((x, i) => (
<input
type="text"
name="book"
className="border mr-px-5"
ref={(event) => {
register(event, rules.book);
if (lastElement) inputRef.current = event;
}}
/>
)}

Generate multiple refs in map loop

You're using non-numeric string keys when adding the refs to itemRefs, which means they end up being properties of the array object, but not array elements, so its length remains 0. Depending on your console, it may or may not show non-element properties on an array object.

You could make them array elements instead by using the index from map (but keep reading!):

{cities.condition.map((condition, index) => (
<div
key={condition.id}
ref={el => (itemsRef.current[index] = el)}
>
Weather: {condition.weather}
<br />
Activity: {condition.activity}
</div>
))}

but depending on what you're doing with those refs I would avoid that in favor of making each condition its own component instead:

const Condition = ({weather, activity}) => {
const itemRef = useRef(null);

return (
<div
ref={itemRef}
>
Weather: {weather}
<br />
Activity: {activity}
</div>
);
};

Then get rid of itemRefs and do:

{cities.condition.map(({id, weather, activity}) => (
<Condition key={id} weather={weather} activity={activity} />
))}

One problem with your current way even if we use array elements is that itemRefs will continue to have three elements in it even when the DOM elements that they used to refer to are gone (they'll have null instead), since React calls your ref callback with null when the element is removed, and your code is just storing that null in the array.

Alternatively, you might use an object:

const itemRefs = useRef({});
// ...
{cities.condition.map(condition => (
<div
key={condition.id}
ref={el => {
if (el) {
itemsRef.current[condition.id] = el;
} else {
delete itemsRef.current[condition.id];
}
}}
>
Weather: {condition.weather}
<br />
Activity: {condition.activity}
</div>
))}

Or perhaps a Map:

const itemRefs = useRef(new Map());
// ...
{cities.condition.map(condition => (
<div
key={condition.id}
ref={el => {
if (el) {
itemsRef.current.set(condition.id, el);
} else {
itemsRef.current.delete(condition.id);
}
}}
>
Weather: {condition.weather}
<br />
Activity: {condition.activity}
</div>
))}

But again, I'd lean toward making a Condition component that manages its own ref.

React useRef with Array for dynamic

You can create multiple refs

function PIN({length, onChange, value}){  const inputRefs = useMemo(() => Array(length).fill(0).map(i=> React.createRef()), []);  const handleChange = index => (e) => {    //onChange(e); // don't know about the logic of this onChange if you have multiple inputs    if (inputRefs[index + 1]) inputRefs[index + 1].current.focus();   }  return (    <div>      {        new Array(length).fill(0).map((inp, index)=>(          <input type="text" ref={inputRefs[index]} onChange={handleChange(index)} />        ))      }    </div>  )}


Related Topics



Leave a reply



Submit