I am still confused if I use useRef([]);
the right way, as itemsRef
returns Object {current: Array[0]}
. Here in action: https://codesandbox.io/s/zealous-platform-95qim?file=/src/App.js:0-1157
import React, { useRef } from "react";
import "./styles.css";
export default function App() {
const items = [
{
id: "asdf2",
city: "Berlin",
condition: [
{
id: "AF8Qgpj",
weather: "Sun",
activity: "Outside"
}
]
},
{
id: "zfsfj",
city: "London",
condition: [
{
id: "zR8Qgpj",
weather: "Rain",
activity: "Inside"
}
]
}
];
const itemsRef = useRef([]);
// Object {current: Array[0]}
// Why? Isn't it supposed to be filled with my refs (condition.id)
console.log(itemsRef);
return (
<>
{items.map(cities => (
<div key={cities.id}>
<b>{cities.city}</b>
<br />
{cities.condition.map(condition => (
<div
key={condition.id}
ref={el => (itemsRef.current[condition.id] = el)}
>
Weather: {condition.weather}
<br />
Activity: {condition.activity}
</div>
))}
<br />
<br />
</div>
))}
</>
);
}
In the original example I receive // Object {current: Array[3]}
when I console.log(itemsRef);
The difference is that I used in my version itemsRef.current[condition.id]
as its a nested map loop and therefore i
doesn't work.
import React, { useRef } from "react";
import "./styles.css";
export default function App() {
const items = ["sun", "flower", "house"];
const itemsRef = useRef([]);
// Object {current: Array[3]}
console.log(itemsRef);
return items.map((item, i) => (
<div key={i} ref={el => (itemsRef.current[i] = el)}>
{item}
</div>
));
}
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.