I've been facing this issue for a couple of days where I have an array named tempTaskArray, which stores objects filled with data taken from my firestore database.
These objects contain the correct data on each iteration of the .forEach method but when put into my state variable, "tasks" (an array), from array tempTaskArray, all objects contain the data of the last created object.
This issue is not run into when I have an array of strings.
See code below:
const getTasks = () => {
db.collection("users").doc(uid).collection("tasks")
.orderBy("epochdate", "asc")
.get()
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// populates array with strings
// tempTaskArray.push(doc.data().taskname)
// tempTaskObject is an empty object
tempTaskObject.taskname= doc.data().taskname
tempTaskObject.duedate= doc.data().duedate
// gets epoch date value and stores it
var epochDate = doc.data().epochdate;
// gets current time
const day = new Date();
let currentTime = day.getTime();
// finds time remaining in epoch
var timeRemaining = epochDate - currentTime;
// finds how many days are left and rounds down
var daysLeft = (timeRemaining / 86400000)
daysLeft = Math.floor(daysLeft)
tempTaskObject.duein = daysLeft
tempTaskArray.push(tempTaskObject)
});
setTasks(tempTaskArray)
})
.catch((error) => {
console.log("Error getting documents: ", error);
});
}
getTasks();
The data taken populating state variable "task" is then rendered in component Task seen below:
<div>
{tasks.map((task) => (
<Task
key={task}
task__title={task.taskname}
due__date={task.duedate}
days__away={task.duein}
/>
))}
</div>
I would be extremely grateful for an explanation of why this is happening. Thanks in advance
CodePudding user response:
Based on the provided code I believe it's an error of adding the reference of the tempTaskObject to the tempTaskArray and changing the same referenced object on each iteration.
To avoid this you can reassign a new Object to tempTaskObject on every iteration, like
.then((querySnapshot) => {
querySnapshot.forEach((doc) => {
// tempTaskObject is (now really) an empty object
tempTaskObject = {};
tempTaskObject.taskname= doc.data().taskname;
Further Explanation
I believe it worked before with String types, because these are primitive datatypes which will just be copied into the tempTaskArray.
The current tempTaskObject though has multiple properties (duein, taskname, duedate) and is therefore a complex object. When pushing objects to an javascript array, it adds the reference to the object to the array, NOT a copy. In your example you always keep the reference to the tempTaskObject and that's why it always changes the properties of the exact same object.
Examples
// Array with Primitives
const animalNames = ['Tiger', 'Spider'];
console.log('animalNames', animalNames);
let scaryName = animalNames[1];
scaryName = 'Goldfish';
console.log('still scary animalNames', JSON.stringify(animalNames)); // nothing changed, because we're working with a copy
// Array with Objects
const animals = [{
name: 'Tiger',
teeth: 30,
legs: 4
}, {
name: 'Spider',
teeth: 0,
legs: 8
}];
console.log('animals', animals);
let scaryAnimal = animals[1]; // storing a reference to the spider object
scaryAnimal.name = 'Goldfish';
scaryAnimal.legs = 0;
console.log('less scary animals', animals); // spider got replaced by a goldfish
// Now we'll want to add another animal the WRONG WAY
// FIX: Uncomment line below to properly add a bunny
// scaryAnimal = {};
scaryAnimal.name = 'Bunny';
scaryAnimal.legs = 4;
scaryAnimal.teeth = 2;
animals.push(scaryAnimal);
console.log('duplicated animal', JSON.stringify(animals));
Edit: Fixed typo