Array Spread Syntax not working as expected on React State Variable - javascript

Hi I'm trying to take some data from my Firebase Firestore and save it to state in my React app. My code is this:
const [firebaseOrders, setFirebaseOrders] = useState([])
useEffect(() => {
const getOrders = async () => {
const querySnapshot = await getDocs(collection(db, `users/carlos#example.com/orders`));
querySnapshot.forEach((doc) => {
const newOrder = doc.data()
setFirebaseOrders([...firebaseOrders, newOrder])
})
}
getOrders();
}, [])
console.log(firebaseOrders)
I am expecting to get an array of objects with my orders. Instead my array is not saving when I use the spread operator so I end up with just the last item in my array:
Here's a pic of what I get in the console
[{…}]
->0 : {titles: Array(2), timestamp: nt, prices: Array(2), amount: 869.99, amount_shipping: 6.99, …}
length : 1
->[[Prototype]] : Array(0)
[{…}]
->0 : {amount: 78.29, timestamp: nt, titles: Array(2), amount_shipping: 0, prices: Array(2), …}
length : 1
->[[Prototype]] : Array(0)
I feel like I am missing something basic, can anyone help me out?

Modifying state is an asynchronous operation, so doing this in a tight loop (as you do) means that you're overwriting the previous value each time.
Instead, call setFirebaseOrders just once with all results:
setFirebaseOrders(
querySnapshot.docs.map((doc) => doc.data());
})
Also note that the console.log(firebaseOrders) may not show the correct value, as it likely runs before setFirebaseOrders(...) has run/completed.
If you want to see the orders from the state, show them in components in your UI. If you want to console.log what you get from the database, do so in the callback:
const orders = querySnapshot.docs.map((doc) => doc.data());
console.log(orders);
setFirebaseOrders(orders);

Related

how to sort and limit firebase realtime db v9?

devs.
I am trying to make a table that will show the top scorers.
I am new to firebase v9.
problem:
I am using realtime DB for storing the name and scores of users with doc names and names are same.
as I used orderbyValue, orderbykey and orderbyChild. snapshot.val() returns an object which contains objects ( check at the bottom ) which is unordered and don't know how to access all or loop.
for limiting, I tried limitToLast which works fine but sorting doesn't.
help me to sort and limit this.
this is how my data is stored in realtime database
this is my code to fetch and try to sort
import { getDatabase, ref, set, onValue, query, orderByChild, limitToLast, equalTo, orderByValue, orderByKey } from "https://www.gstatic.com/firebasejs/9.4.1/firebase-database.js";
var namelist = $('.person');
var list = $('.scores');
// const scoreBoardRef = ref(db, 'users');
const scoreBoardRef = query(ref(db, 'users'), orderByChild('score'));
onValue(scoreBoardRef, (snapshot) => {
const data = snapshot.val();
console.log(data); // this returns object containing object and **i don't know how to deal with this.**
//console.log(scoreBoardRef);
list[0].textContent = data.score; // here i want score
namelist[0].textContent = "";//here i want name
//my main goal is to loop through top 3 or 10 data and print it to table
});
In the console, this is what I get
⬇{abcd: {…}, man2: {…}, nacho: {…}, two: {…}}
▶abcd: {name: 'abcd', score: 15}
▶man2: {name: 'man2', score: 20}
▶nacho: {name: 'nacho', score: 21}
▶two: {name: 'two', score: 18}
▶[[Prototype]]: Object
As Dharmaraj commented: the results in the snapshot are actually ordered, but when you call snapshot.val() it gets converted to a JSON object and the keys in a JSON object are by definition not ordered.
To get the results in order, loop over them with snapshot.forEach:
onValue(scoreBoardRef, (snapshot) => {
snapshot.forEach((child) => {
const data = child.val();
console.log(data);
});

ES6 change to fixed values in API request

How do I change the values of my object to use the fixed values as I have done with my console.log?
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data
apiResponse.forEach(employee => console.log('test', employee.value.toFixed(2)))
apiResponse.forEach(employee => employee.value.toFixed(2))
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse,
})
})
}
test 4.41
test 5.00
test 6.16
test 0.79
raw data
(10) [{…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}, {…}]
0: {name: "Animals", value: 4.41361634}
1: {name: "Environment", value: 5.004498622999998}
The error with your code is that you are just formatting the value number and returning it to forEach (that does nothing with this information).
You must modify the original object.
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data;
apiResponse.forEach(employee => {
employee.value = employee.value.toFixed(2); // Note we are attributing the fixed value to the value itself, modifying the object
});
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse
})
})
}
Note that this works because JavaScript always use a reference for objects (like a pointer in C), so even without returning the object, you are changing it properties and it will reflect on the original array.
IMO is more readable for non experienced (and experienced as well) programmers using Array.map() instead, since it will make clear that you are modifying the object and updating the array:
fetchData = () => {
axios.get(fullAPI).then(res => {
const apiResponse = res.data;
apiResponse.map(employee => {
employee.value = employee.value.toFixed(2)
return employee;
});
console.log('raw data', apiResponse)
this.setState({
employeeData: apiResponse
})
})
}
Both codes will do the same, probably no performance differences, just different readability.
You need to set employee.value to the desired value. Additionally, I recommend not using the implicit return syntax and use braces for readability.
apiResponse.forEach(employee => {
employee.value = employee.value.toFixed(2);
});

undefined on getting specific value from State array - React Native

I'm reading data from firestore and stores it in state array of objects.
when i
console.log(this.state.array)
it returns the whole array with all the data of the objects, but when i
console.log(this.state.array.name)
or
console.log(this.state.array[0])
it returns undefined
.
I have tried to get the data with
forEach
loop but it seems to be not working as well.
constructor(props) {
super(props);
this.state = { tips: [] };
}
componentDidMount() {
firebase.firestore().collection('pendingtips').get()
.then(doc => {
doc.forEach(tip => {
this.setState([...tips], tip.data());
console.log(this.state.tips);
});
})
.catch(() => Alert.alert('error'));
}
renderTips() {
console.log(this.state.tips); //returns the whole array as expected
console.log(this.state.tips[0].name); //returns undefined
return this.state.tips.map(tip => <PendingTip key={tip.tip} name={tip.name} tip={tip.tip} />); //return null because tip is undefined
}
render() {
return (
<View style={styles.containerStyle}>
<ScrollView style={styles.tipsContainerStyle}>
{this.renderTips()}
</ScrollView>
</View>
);
}
the array structure is:
"tips": [
{ name: "X", tip: "Y" },
{ name: "Z", tip: "T" }
]
so I expect this.state.tips[0].name will be "X" instead of undefined.
thanks in advance.
First of all you should fetch data in componentDidMount instead of componentWillMount.
https://reactjs.org/docs/faq-ajax.html#where-in-the-component-lifecycle-should-i-make-an-ajax-call
Secondly, you should use this.setState to update your state, instead of mutating it directly.
componentDidMount() {
firebase
.firestore()
.collection("pendingtips")
.get()
.then(docs => {
const tips = docs.map(doc => doc.data());
this.setState({ tips });
})
.catch(() => Alert.alert("error"));
}
I Found out that the problem was that JavaScript saves arrays as objects.
for example this array:
[ 'a' , 'b' , 'c' ]
is equal to:
{
0: 'a',
1: 'b',
2: 'c',
length: 3
}
"You get undefined when you try to access the array value at index 0, but it’s not that the value undefined is stored at index 0, it’s that the default behavior in JavaScript is to return undefined if you try to access the value of an object for a key that does not exist."
as written in this article
firesore requests are async, so by time your request gets execute your component is getting mounted and in a result you are getting undefined for your state in console.
You must do API call in componentDidMount instead of componentWillMount.
Mutating/changing state like this, will not trigger re-render of component and your component will not get latest data,
doc.forEach(tip => {
this.state.tips.push(tip.data());
console.log(this.state.tips);
});
You must use setState to change your state, doing this your component will get re-render and you have latest data all the time.
componentDidMount(){
firebase.firestore().collection('pendingtips').get()
.then(doc => {
const tipsData = doc.map(tip => tip.data());
this.setState({tips:tipsData},() => console.log(this.state.tips));
})
.catch(() => Alert.alert('error'));
}
While calling renderTips function make sure your state array has data,
{this.state.tips.length > 0 && this.renderTips()}

How to access properties from a Javascript object?

I have an api call that produces the follow results in the console (after pairing it down using map() ).
{…}
​
CHANGE24HOUR: "$ 11.16"
​
CHANGEDAY: "$ 3.31"
​
CHANGEPCT24HOUR: "6.73"
​
CHANGEPCTDAY: "1.90"
​
FROMSYMBOL: "Ξ"
​
HIGH24HOUR: "$ 183.38"
​
HIGHDAY: "$ 183.38"
However, no matter what I try I can't get at it's properties. The object is called 'coinStats'.
I have tried the following:
coinStats.HIGHDAY = undefined
coinStats['HIGHDAY'] = undefined
coinStats["HIGHDAY"] = undefined
Tried to convert it to an array to see if that would help using
Object.values(coinStats) // Would not work
I am sure the answer is so simplistic. But I am just not sure what it is?
The original raw api results are in the following shape:
(1) […]
​
0: Object { CoinInfo: {…}, RAW: {…}, DISPLAY: {…} }
​
length: 1
​
<prototype>: [
The info I am looking for is in DISPLAY -> USD. I used a map() function to return that sub-object.
The code I am using to fetch the data is essentially the following:
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === TRX});
})
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.dir(coinStats);
I can't access coinStats.HIGHDAY for example... I get 'undefined'.
The fetch call is async, so you can't access the view variable until the call is finished. The code calls the async function and runs the next line, so view variable will be undefined. You have to handle the data inside the fetch callback function, where the data is known.
const API = 'https://min-api.cryptocompare.com/data/top/mktcapfull?tsym=USD&page=1';
fetch(API)
.then(results => results.json())
.then(coinData => {
const view = coinData.Data.filter(obj => {
return obj.CoinInfo.Name === 'TRX'
});
const coinFeedAbridged = view.map(item => item.DISPLAY.USD);
const coinStats = coinFeedAbridged[0];
console.log(coinStats);
})
}
You can test it out in this fiddle. https://jsfiddle.net/gran7ptk/1/
Change line - Add inverted commas to Text "TRX".
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name
=== TRX});
to
const view = coinData.Data.filter(obj => { return obj.CoinInfo.Name === "TRX"});

Querying data from Firebase collection

I have been working in the LAMP stack for years and am struggling to wrap my head around advanced querying with Firebase and NoSQL databases. I would like to return 5 random documents from a collection. Below is the VueJS code written thus far:
Here is the data object that I have created:
data () {
return {
courseIds: [],
}
}
Here is my created lifecycle hook where I'm querying the Firebase NoSQL database:
created() {
// fetch data from firestore
database.collection('courses').get()
.then(snapshot => {
snapshot.forEach(doc => {
let course = doc.data()
course.id = doc.id
this.courseIds.push(course.id)
})
})
}
Because I'm looking to randomize the data returned, I have added the beforeMount lifecycle hook which calls a Fisher-Yates shuffle method. The plan was to shuffle the returned data and then return only the first 5 documents:
beforeMount() {
this.courseIds = this.shuffle(this.courseIds)
}
And the method:
methods: {
shuffle: function(array) {
var m = array.length, t, i;
while (m) {
i = Math.floor(Math.random() * m--);
t = array[m];
array[m] = array[i];
array[i] = t;
}
return array;
}
}
I'm a slow learner, so more than likely this is a numbskull approach to the problem. My present issue is that Firebase is returning the Ids as an object rather than an array so my shuffle method isn't working as anticipated. See the console.log() below:
[__ob__: Observer]
0: "0HnqJ8zZg1Rs3D4qod4l"
1: "1gZmoUpCOSDeLsYMDi4v"
2: "JrJj3a84qKTD72ncvGXd"
3: "LWMbY98m3sKLrHNDSUkW"
4: "SUn1kxHzMo7fu5urpNB5"
5: "kQRWQIj0mFXIWVJcaouY"
length: 6
__ob__: Observer {value: Array(6), dep: Dep, vmCount: 0}
__proto__: Array
Does anybody have a better approach to this functionality, or a good way to convert the courseIds into an array to be shuffled?
What happens if you do a bit differently, as follows:
created() {
// fetch data from firestore
database.collection('courses').get()
.then(snapshot => {
let courseIdsArray = [];
snapshot.forEach(doc => {
courseIdsArray.push(doc.id);
});
this.courseIds = courseIdsArray;
});
}

Categories