What's is this behavior of JavaScript Array? - javascript

I'm started to work with JavaScript/Firestore for a few days but i'm facing some issues with the behavior of the laguage in array's. I cannot run array's with a for loop inside a function and almost all the time it returns me an undefined. this is my code:
This is the code who takes all the id's on firestore who the meeting is active :
function validation () {
let path = firebase.firestore()
path
.collection('database')
.doc('405')
.collection('meetings')
.where('active', '==', true)
.get().then( snapshot =>{
snapshot.docs.forEach( snapshot => {
this.id_firestore.push(snapshot.id)
})
})
},
And this is the code who runs all the id's on id_firestore array and takes all the doubts of the meeting on firestore:
function search_doubts (){
let data = this.id_firestore // in this line data.value = ["QEq1VexdC28BbWRvSFL7","vFsSdDeHJqJQU13dwMwQ"]
let path = firebase.firestore().collection('database').doc('405').collection('meeting')
console.log(data) // here it logs me all the array normally
console.log(data[0]) // here the log returns me undefined
for (let i = 0; i<data.lenght; i++) {
path.doc(data[i]).collection('doubts').get().then( snapshot =>{
snapshot.docs.forEach( snapshot => {
this.doubts.push(snapshot.data().txt_doubts)
this.id_firestore_doubts.push(snapshot.id)
})
console.log(data[1]) //here it logs me the data normally
})
}
}
I did too many searchs but i didn't find anything about this behavior, can anybody answer why the function is behaving like this?
This is the chrome console result's on my debbug:
Console.log(data):
[__ob__: Observer]
0: "QEq1VexdC28BbWRvSFL7"
1: "vFsSdDeHJqJQU13dwMwQ"
length: 2
__ob__: Observer {value: Array(2), dep: Dep, vmCount: 0}
__proto__: Array
Console.log(data[0]):
undefined
Console.log(data[1])
vFsSdDeHJqJQU13dwMwQ

Found the answer. I'm did this functions in a vue enviorment and because and under the table the variable runs observer standards. I Just have to make a internal var and run the other function inside it:
function validation () {
let id_firestore = []
let path = firebase.firestore()
path
.collection('database')
.doc('405')
.collection('meetings')
.where('active', '==', true)
.get().then( snapshot =>{
snapshot.docs.forEach( snapshot => {
this.id_firestore.push(snapshot.id)
})
this.search_doubts (id_firestore)
})
},
Thank's for the help!

Related

Vuex Store Object Child returns undefined, parent returns properly. Why?

I've seen some similar questions but they don't seem to match my situation.
When I log this.$store.state.account I get the expected result
{__ob__: Nt}
user: Object
favorites_playlist: (...)
firebaseID: (...)
spotifyID: (...)
However, when I console.log(this.$store.state.account.user) the user object disappears! All of the nested properties inside user return undefined though they log perfectly fine above
console.log(this.$store.state.account.user)
{__ob__: Nt}
__ob__: Nt {value: {…}, dep: vt, vmCount: 0}
__proto__: Object
This is the method inside my component calling the object
async connectToSpotify() {
console.log("User Profile: ", this.user)
var firebaseID = await this.$store.dispatch("signInToFirebase")
var authID = await this.$store.dispatch('connectToSpotify')
Vue.set(this.$store.state.spotify, "authId", authID)
var userProfile = await this.$store.dispatch("fetchUserDataFromSpotify", authID)
userProfile["firebaseID"] = firebaseID
this.$store.dispatch("bindCurrentUser", userProfile.spotifyID)
console.log("this.$store.state")
console.log(this.$store.state);
console.log("this.$store.state.account")
console.log(this.$store.state.account);
console.log("this.$store.state.account.user")
console.log(this.$store.state.account.user);
console.log("this.$store.state.account.user.favorites_playlist")
console.log(this.$store.state.account.user.favorites_playlist);
// console.log(this.$store.state.account.user.firebaseID);
var favorites_playlist = this.$store.state.account.user.favorites_playlist
var playlistID = await this.$store.dispatch("createFavoritesPlaylist", [authID, userProfile.spotifyID, favorites_playlist])
console.log(`PlaylistID: ${playlistID}`);
userProfile["favorites_playlist"] = playlistID
console.log(this.$store);
return db.ref(`users/${userProfile.spotifyID}`).update(userProfile)
},
this is the action inside my accounts module that binds the user to firebase
const state = () => ({
//user: { voted_tracks: {}, favorited_tracks: {}, favorites_playlist: null, authID: null}
user: {},
})
const actions = {
bindCurrentUser: firebaseAction(({ bindFirebaseRef }, id) => {
return bindFirebaseRef('user', db.ref('users').child(id))
}),
}
Not sure what further information would be relevant aside that this.$store.state.account.user is binded via Vuexfire to a database reference. The store is injected into the root component
Your data comes in after the console.log. The console updates object/array logs with current values when you click, but can't do that with primitives. See this answer for more detail.
It should be enough to await the firebaseAction:
await this.$store.dispatch("bindCurrentUser", userProfile.spotifyID)
bindCurrentUser: firebaseAction(({ bindFirebaseRef }, id) => {
console.log("account.bindCurrentUser() called");
return bindFirebaseRef('user', db.ref(`users/${id}`)).then(() => {
console.log("account.bindCurrentUser() -- complete")
}).catch((err) => {console.log(err)})
}),

forEach not a function when index starts at anything but 0

When inside an object there isn't an index that starts with 0, it returns:
TypeError: data.forEach is not a function
This is how the database looks:
If I add an object with the index 0 to the database, like so (my formatting doesn't matter, this is just to illustrate the hierarchy):
0: {
email: "testmail",
uid: "testuid"
}
Suddenly the forEach function works and also retrieves the users with index 3 and 4. How can I make the forEach loop start at index 3 for example? Or is there a different method that I should be using instead? My code:
useEffect(() => {
if(props.klasData.gebruikers !== undefined) {
var data = props.klasData.gebruikers;
data.forEach(function (user) {
if(!emails.hasOwnProperty(user.email)) {
addEmail(oldArray => [...oldArray, user.email]);
}
setPending(false)
})
}
}, []);
Edit
props.klasData.gebruikers returns all keys within the "gebruikers" object with their children.
It looks like your data is being interpreted as an array by the Firebase SDK, since it starts with sequential numeric keys. If you print the value of your gebruikers snapshot, you'll see that it's:
[null, null, null, {email: "test", uid: "test"}, {email: "wouter#...", uid: "..."}]
These null values are added by the Firebase SDK to turn the keys into a proper array.
If you want to keep the Firebase SDK from converting the data into an array, prefix the keys with a non-numeric character. For example:
"key2": {email: "test", uid: "test"},
"key3": {email: "wouter#...", uid: "..."}
In general, it is more idiomatic to use the UID of the users as the keys in a collection of users. That way, you won't need to query for the UID, and you're automatically guaranteed that each user/UID can only be present in the collection one.
I changed the database like you can see here, as Frank van Puffelen suggested. As Frank also predicted, the root of my problem was coming from the function I didn't post.
By transforming all the indexes of UIDs I was fetching from the database to sequential numeric keys, I managed to get the forEach function working. I did this using users = Object.values(users).filter((el) => {return el != null}). The full effect hook can be found below:
useEffect(() => {
var refCodes = firebase.database().ref("userdata/" + currentUser.uid + "/docentCodes").orderByKey();
refCodes.once("value").then(function (snapshotCodes) {
snapshotCodes.val().forEach(function (code) {
var refCodeData = firebase.database().ref("klassencodes/" + code).orderByKey();
refCodeData.once("value").then(function (snapshotCodeData) {
var users = snapshotCodeData.val().gebruikers;
users = Object.values(users).filter((el) => {return el != null})
if(snapshotCodeData.val() !== null) {
setUsercount(oldArray => [...oldArray, users.length]);
setKlasData(oldArray => [...oldArray, snapshotCodeData.val()]);
setUserdata(oldArray => [...oldArray, users]);
addCode(oldArray => [...oldArray, code])
}
setPending(false);
})
})
})
}, []);
In the function where this useEffect is used, I added const [userdata, setUserdata] = React.useState([]); to acommodate this new information stripped down from indexes of UIDs to indexes made of numeric keys. This userdata is exported to another function, which has the effect hook as stated in the original question. I changed this up to be:
useEffect(() => {
if(props.userData !== undefined) {
var data = props.userData;
if(data !== undefined) {
data.forEach(function (user) {
if(!emails.hasOwnProperty(user.email)) {
addEmail(oldArray => [...oldArray, user.email]);
addUID(oldArray => [...oldArray, Object.keys(props.klasData.gebruikers)]);
}
setPending(false)
})
}
}
}, []);
Summary
In retrospect, I should've gone with a seperate const for just the userdata (snapshotCodeData.val().gebruikers), seperate from the other data returned from the snapshot (snapshotCodeData.val()).
I hope this may help you. The golden line of code is users = Object.values(users).filter((el) => {return el != null}).

Array length is zero but Array is not empty

When I'm trying to iterate through an array, get it's length or access indexes I'm getting Error TypeError: Cannot read property 'map' of undefined.
The array isn't empty and when I console.log() it I've gotten.
0: {user_id: 11, …}
length: 1
__proto__: Array(0)
I see that proto: Array(0) and I'm assuming this means it's a 0 length Array but how do I make it non-zero length so that I can iterate through it?
Code for reference:
useEffect(() => {
blog.authors.map(data => {
console.log(data)
})
}, [blog])
I've also tried. It worked, but I immediately got the similar error.
useEffect(() => {
(async() => {
await blog.authors.map(data => {
console.log(data)
})
})()
}, [blog])
The simple checking if blog.authors isn't undefined solved it.
No, array indexes are 0 based.
var arr = ['a', 'b'];
console.log(arr.length); // => 2
console.log(arr[0]); // => a
So instead of making confusing assertion about what works and not without providing the code them providing other code, could you just see the result of that:
useEffect(() => {
console.log({blog});
console.log({authors: blog.authors });
blog.authors.map(data => {
console.log(data)
})
}, [blog])
Because I suspect that blog changed overtime, having some authors and something authors undefined.

fetch gives me a full array but it is empty when i try to access it

I get an object from a Symfony rest API. This object has a property "shooting" which is an array and this array is full when I console.log it but when i try to access it, it is empty
This is my fetch request
const getProjectsAvailable = async () => {
const data = await fetch(
`${process.env.GATSBY_CORE_URI}/api/dashboard/supplier/projects/available`,
{
headers: {
[`X-Auth-Token`]: `${token}`,
[`Accept`]: `application/json`,
},
}
);
return data;
};
Here is the project object that i get back from fetch request
0: {id: 258, name: "Project26-1", reference: "A6568", isOfferValidated: null, source: "dashboard", …}
It has a shooting key which contains an array and it is not empty
shootings: Array(1)
0:
addressCity: "Paris"
addressCountry: {id: 76}
But when i set this object to my component state, all values stay the same except the shooting key which becomes an empty array
const [projects, setProjects] = useState([]);
useEffect(() => {
getProjectsAvailable().then(res =>
res.json().then(data => {
setProjects(data);
})
);
}, []);
I have no idea why does it act like that.
Thanks in advance
EDIT :
For example, the first line with console.log gives me the response object with a full shooting array while the second one sets it to my state but shooting array is empty
useEffect(() => {
getProjectsAvailable().then(response => console.log(response));
getProjectsAvailable().then(response => setProjects(response));
}, []);
Ok it is my bad. Somewhere else in the code, there was a .split() on the shooting property which mutates the array so the props changed and shooting array got empty

Why am I getting back "undefined" from my Axios call, when it should return me objects?

Problem Intro:
In my React app I'm making some API calls to Github to fetch some user data. In another function I call these functions and wait for it with Axios's .all() method.
I want to fetch the data and do something with it using .then() but the returned value is just an array with 2 times undefined
What is the expected thing to happen:
It should return me 2 player objects with profile infos in the profile key of the object and a score value in the score key of the object.
What is my app doing in a nutshell?
It fetches the data from 2 usernames and they can "battle" each other. It just fetches the score and the followers and returns a sum. At the end it (should) returns an array with 2 player objects already sorted with the winner in the first place (exampleArray[0]).
General information's
It's an react app using components. It's really about one very tiny component and the helper functions in another file.
Here is where I call my custom function (the one returning undefined):
componentDidMount() {
const players = queryString.parse(this.props.location.search); //<== the usernames
const playersArray = [players.playerOneName, players.playerTwoName];
console.log(playersArray); // <== this logs the output as expected (an array with 2 usernames)
battle(playersArray).then((data) => { // <== the function it's all about
console.log(data); // <== data is => [undefined, undefined];
})
}
Next is the battle function from above that uses 2 other functions:
battle
export function battle(players) { // <== players aray with 2 usernames as string
return axios.all(players.map(getUserData)) // <== look 1 function below
.then(sortPlayers) // <== two functions below
.catch(handleError)
}
getUserData
let getUserData = (player) => {
axios.all([
getProfile(player),
getRepos(player)
]).then((data) => {
return {
profile: data[0],
score: calculateScore(data[0], data[1])
}
})
}
sortPlayers
let sortPlayers = (players) => {
return players.sort((a, b) => {
return b.score - a.score;
})
}
Ok so they also use other functions but they are really not too complicated. Let me know when you need examples from the other little helpers too.
I tried it with placing the debugger in different spots in the code and console logged different things, but I can't come through (first time I'm really working with promises). Sitting now 2 hours in front of this tiny problem and I can't figure it out.
I think the problem lies somewhere in battle function itself or getUserData
At the end a little screenshot, what the output of my console.log looks: http://prntscr.com/hz5abq
Thanks in advance
You don't have anything being returned in getUserData . Either add a return or remove the {} wrapping axios.all
let getUserData = (player) => {
return axios.all([
getProfile(player),
getRepos(player)
]).then((data) => {
return {
profile: data[0],
score: calculateScore(data[0], data[1])
}
})
}
getUserData needs to return the promise that it creates. At the moment it's not returning anything, so an implicit undefined is returned, and thus players.map(getUserData) results in an array of [undefined, undefined]
Ie, do this:
let getUserData = (player) => {
// VVV added return statement
return axios.all([
getProfile(player),
getRepos(player)
]).then((data) => {
return {
profile: data[0],
score: calculateScore(data[0], data[1])
}
})
}

Categories

Resources