I'm currently creating a react.js app with function components. I want to store some urls from my cloud storage inside a map data structure, so I can access them later in my html code. The problem is, that the values are not stored properly and I end up with an empty map, as you can see in the screenshot:
Log output of: console.log("imageMap", imageMap) and console.log("imageMap", imageMap.values())
But inside my function the map is correct...
log output of console.log("tmp", tmpMap)
Does anyone has an idea how to fix this? Here is my code:
const [imageMap, setImageMap] = useState(new Map());
useEffect(() => {
const func = async () => {
let tmpMap = imageMap
const reference = ref(storage, "target-content")
await listAll(reference)
.then((res) => {
res.items.forEach((itemRef) => {
getDownloadURL(itemRef).then((x) => {
tmpMap.set(itemRef.name, x)
console.log("tmp", tmpMap) //ok size: 18
setImageMap(tmpMap)
}
);
});
}).catch((error) => {
// Uh-oh, an error occurred
});
}
func();
console.log("imageMap", imageMap) //size: 0, entries: 18?
console.log("imageMap", imageMap.values())
}, []);
CodePudding user response:
The map is correct in console.log because the browser remembers its reference and shows you a modified value. It happens because you actually mutate your imageMap; assigning it to tmpMap doesn't achieve what you'd like: imageMap is still being mutated.
React also doesn't recognize imageMap updated and doesn't rerender your component as you'd expect.
You could do let tmpMap = new Map(imageMap)
to alleviate that, it's one of the ways to fix it.
CodePudding user response:
Your function func()
is executed asynchronously while the following statements are executed. This means, that console.log("imageMap", imageMap)
is executed (potentially) before tmpMap.set(...)
is called in your asynchronous function.
The second problem is that setImageMap(tmpMap)
does not do anything - it will will just replace a pointer to a map with the same pointer. You should instead create a new Map and assign imageMap
to the newly created Map.
You can verify this by adding another useEffect hook to your code that reacts to changes is the imageMap
state variable:
useEffect(() => {
console.log("imageMap", imageMap);
console.log("imageMap", imageMap.values());
}, [imageMap]);
You will see that eventually, imageMap
will be assigned to a new value.