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 ref
s can actually use as function and then set two ref
s 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
How to Update State.Item[1] in State Using Setstate
How to Chop/Slice/Trim Off Last Character in String Using JavaScript
How to Dodge Jquery Promises Completely When Chaining Two Async Jquery Functions
Get Unique Selector of Element in Jquery
How to Use Greasemonkey to Selectively Remove Content from a Website
Trigger Standard HTML5 Validation (Form) Without Using Submit Button
Setting Href Attribute at Runtime
How to Play a Video in a Webview with Android
Jquery Slider, How to Make "Step" Size Change
Is It Safe to Store a Jwt in Localstorage with Reactjs
Check If a Variable Is a String in JavaScript
Unexpected Token Colon JSON After Jquery.Ajax#Get
How to Get Jquery to Select Elements with a . (Period) in Their Id
How to Use JavaScript to Read Local Text File and Read Line by Line
Sharing a Variable Between Multiple HTML Pages