React State is Changing Without Using setState() - javascript

I'm developing a quiz/test website. I want to see different questions when I move to next question and don't want to see same answers at the same time.
I have a state array varaible which is calling allWords. This state
will keep all words.
And I have another state array variable which calls like
neverAskedWords. This state will keep words which never used
always.
I'm creating a new array variable and defining with allWords in a function. When I'm removing any record in the new array variable then that record is removing in allWords variable as well... Why?
I want to remove any record in that temporary array and want to save updated version to neverAskedWords state. In this way I could see different questions always. Here is my codes.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
let neverAskedList = await wordsList //creating and defining temporary variables
const allWordsList = await wordsList //creating and defining temporary variables
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
allWords should'nt change. But changing, because of why?

So the most obvious thing that I see in your code is that you are modifying the same object. What you should do instead is use the spread operator.
const [allWords, setAllWords] = useState([])
const [neverAskedWords, setNeverAskedWords] = useState([])
async function getAllData(){
axios
.get(`http://127.0.0.1:3000/api/improve-language`)
.then(res => {
setAllWords(res.data)//defining allWords
setNeverAskedWords(res.data)//defining neverAskedWords
firstQuestionAndAnswers(res.data)//sending all datas by parameter, bacause when I'm trying to get datas by using `allWords` state, it would be undefined. That's why sending all data by parameter for the first time to set first question and answers.
})
.catch(err =>{
console.log(err)
})
}
async function firstQuestionAndAnswers(wordsList){
// don't use await for js objects, should be used only with promises.
// use spread operator to make copy of the wordList array so you never actually modify the original object
let neverAskedList = [...wordsList]
const allWordsList = [...wordsList]
//some not necessary codes for this issue
const questionIndex = randomNumber(neverAskedList.length)
const firstQuestion = neverAskedList[questionIndex]
let firstAnswers = []
for (let i = 0; i < 4; i++) {
let answerIndex = randomNumber(allWordsList.length)
firstAnswers[i] = allWordsList[answerIndex]
allWordsList.splice(answerIndex, 1)//and here! I'm removing this record to prevent using it again next time, there will be different answers always
}
//some not necessary codes for this issue
firstAnswers.push(firstQuestion)
const randomisedAnswers = firstAnswers.sort(()=>Math.random() - 0.5)
//some not necessary codes for this issue
setQuestion(firstQuestion)
setAnswers(randomisedAnswers)
//and then here! I'm removing the used question in this time to prevent using it again, there will be different questions always and never see this question again
neverAskedList.splice(questionIndex, 1)
setNeverAskedWords(neverAskedList)
}
If you don't understand why it happened then here's a short explanation. In js when you do const a = { key: 'val' } you created a variable that references the memory block that is actually storing your object. And when you do const b = a you are creating another variable that references the same memory block. So updating 1 automatically changes the other one.

Related

Properly append to an array when handling presses

Within my function, through interaction from the user, I aim slowly build up an array of responses which I then pass off to an API. However, different approaches to append to the array, simply return a single position array (overwrite).
My current code as follows:
const contribution: Array = [];
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
contribution = [...contribution, col];
}
My understanding is that contribution = [...contribution, col] is the correct way to add to the array.
What is the best practice approach for doing this inside a function called each time the user interacts?
Although it is not clear from the question, I suspect, this code is inside a component. If so, then a new contribution array is created on every render. You need to use useState to store this array so that a new array is not created on every render.
const [contribution, setContribution] = React.useState([]);
const handlePress = () => {
var col = {
response,
user: 1,
update: update.id,
question: q.id,
};
setContribution([...contribution, col]);
}

Persist useState In For Loop ReactJS

I have a form that dynamically enters elements to a react state array on click, and obviously between clicks the state persists. I am trying to now do the same thing programatically but in each iteration the state does not persist, is the only answer to this truly a context object or local storage or is there something wrong with my iteration that I can correct to allow state to persist.
Ive simplified the code basically the button firing will add as many elements as I want but trying to tell react to create 3 elements via the for const only creates 1. I have scripts to write state to session storage, so if there's not some big thing i'm missing, I'll probably just do that, but i figure I'd ask and see cause it would drastically improve the overall health of my app if i knew the solution to this.
const sectionI = {
type: "i",
sectionArea: "",
};
const [i, setI] = useState([])
const strArr = ["i","i","i"]
const addI = () =>{
const newI = [...i, {...sectionI}]
setI(newI)
}
<button onClick={()=>addI()}>Add One Image</button>
const addMultiple = () =>{
for(const el of strArr){
const newI = [...i, {...sectionI}]
setI(newI)
}
}
I will show you how to fix it and give you a link to another one of my answers for the explanation. Here is how to fix the issue:
const addMultiple = () =>{
for(const el of strArr){
setI(prevState => [
...prevState,
{...sectionI},
])
}
}
And here is why it is happening: https://stackoverflow.com/a/66560223/1927991

How to "control" when Firestore.getAll(...promises) stops by reject

Context:
I 'm doing a cloud function to send pushes to multiple users. I need to recover the info of each user to know some data like, name, country..etc..
Problem:
Actually I recover the list of user Id's and when I got it, then I create an array of promisesto recover all the info:
var usersPromises = []
for (var i = 0; i < usersInRange.length; i++) {
usersPromises[i] = firestore.collection("users").doc(usersInRange[i])
}
Then I recover and send the push using firestore.getAll():
firestore.getAll(...usersPromises).then(results => {
for(snapshot in results){
if(snapshot.exists){
......
var user = snapshot.data()
......
}else{
......
}
}
})
This solution is actually working "fine" almost all the time. But at this moment the Firestore db has some users that do not exist or something is wrong, because the method getAll()stops before finishing all the promises. I know it because no push is sent, and in the console, just say that the method has finished.
Reading in SO and documentation, I saw, that getAll stops if some promise is "broken". (all or nothing)
And here is where I'm lost. How can I "force" or do in another way, to just "jump" this promises that can't be completed?
P.S:
I tried to do with a "for" but It seems to omit some promises:
for (var i = 0; i < usersPromises.length; i++) {
usersPromises[i]
.get()
.then(snapshot => {
if(snapshot.exists){
......
var user = snapshot.data()
......
}else{
......
}
})
}
I think its not a problem of getAll. I have tested like this:
const firestore = new Firestore();
let doc = []
doc[0] = firestore.doc('test/test');
doc[1] = firestore.doc('test/test1');
doc[2] = firestore.doc('test/doc');
firestore.getAll(...doc)
.then(result=> result.forEach(doc => console.log(doc._fieldsProto)))
.catch(err=>console.log(err));
In my database I have 'test/test' and 'test/doc' document, but I do not have 'test/test1' and results look like this:
So we just get undefined on document that is not exist and that's all. I suggest to add catch and see if there is any exception. When I have been writing the test the function was interrupted by typo mistake in inner function.
I hope this will help!

Get the ChildrenCount of a child in Firebase using JavaScript

I have been doing this for an hour. I simply want to get the number of children in the child "Success" in the database below. The answers in similar stackoverflow questions are not working. I am new in Javascript Programming.
So far I have tried this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
and also this
var children = firebase.database().ref('Success/').onWrite(event => {
return event.data.ref.parent.once("value", (snapshot) => {
const count = snapshot.numChildren();
console.log(count);
})
})
Where might I be going wrong.
As explained in the doc, you have to use the numChildren() method, as follows:
var ref = firebase.database().ref("Success");
ref.once("value")
.then(function(snapshot) {
console.log(snapshot.numChildren());
});
If you want to use this method in a Cloud Function, you can do as follows:
exports.children = functions.database
.ref('/Success')
.onWrite((change, context) => {
console.log(change.after.numChildren());
return null;
});
Note that:
The new syntax for Cloud Functions version > 1.0 is used, see https://firebase.google.com/docs/functions/beta-v1-diff?authuser=0
You should not forget to return a promise or a value to indicate to the platform that the Cloud Function execution is completed (for more details on this point, you may watch the 3 videos about "JavaScript Promises" from the Firebase video series: https://firebase.google.com/docs/functions/video-series/).
const db = getDatabase(app)
const questionsRef = ref(db, 'questions')
const mathematicalLiteracy = child(questionsRef, 'mathematicalLiteracy')
onValue(mathematicalLiteracy, (snapshot) => {
const data = snapshot.val()
const lenML = data.length - 1
console.log(lenML)
})
This method worked for me. I wanted to get the children's count of the mathematicalLiteracy node in my database tree. If I get its value using .val() it returns an array that contains that node's children and an extra empty item. So, I subtracted that one empty item's count. Finally, I get my needed children's count.

Issue creating an array of Firestore objects

It seems like nothing is stored in the array with this code:
(Assume showList is an actual div id; didn't think it was necessary to include those tags in the code)
var i = 0;
var array = [];
var showList = document.getElementById('showList');
firebase.firestore().collection('TV Shows').get().then(snapshot => {
snapshot.forEach(doc => {
array[i] = doc.data().show.name;
i++;
//console.log(doc.data().show.name);
});
});
showList.innerHTML = array[4];
Funny enough, that commented console.log line works fine and displays all the objects, so I don't understand why I can't store it in the array. A big part of me feels as if it's my direction in using the array and/or i variable, but I tagged it as a firebase/firestore issue as well on the off chance it turns out to be a firebase issue.
If someone could point me in the write direction, I would appreciate it.
.get() is asyncronyous, and at the time you run:
showList.innerHTML = array[4];
array has not been initialized yet.
You either need to use callbacks/promises as mentioned in the linked duplicate, or simply move your call inside the .then() function:
var showList = document.getElementById('showList');
firebase.firestore().collection('TV Shows').get().then(snapshot => {
var array = [];
var i = 0;
snapshot.forEach(doc => {
array[i] = doc.data().show.name;
i++;
//console.log(doc.data().show.name);
});
showList.innerHTML = array[4];
});

Categories

Resources