About setState in React - reactjs

Below is a function getting messages from firebase database, but it only setState to only one message.
However, the console.log can log multiple messages in the object.
Is there anything wrong in my function?
getMessages(){
var messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
//alert(JSON.stringify(messages));
messages.push({
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
});
this.setState({
messages: messages
});
console.log('woooo'+JSON.stringify(messages));
});
});
});
}

You are setting the state inside the forEach Block. try moving it outside the iteration block
As JanneKlouman mentioned it's not good enough to remove it from the iterration block as you are doing async calls.
You can create a new array and set it in the state on each iteration, react will batch those set state calls:
function getMessages() {
var messages = [];
firebaseApp.database().ref('users/' + firebase.auth().currentUser.uid + '/userChat/' + firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
//alert(JSON.stringify(dataSnapshot.val()));
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message) => {
const newMessage = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
}
const nextState = this.state.messages.map(message => {
return {
...message,
user: {...meesage.user} // i think we must do this in order to break out of the reference as spreading will only work on a shallow level
}
});
this.setState({
messages: [...nextState, newMessage]
});
});
});
});
}

Try cloning the array before setting state:
getMessages(){
let messages = [];
firebaseApp.database().ref('users/'+firebase.auth().currentUser.uid+'/userChat/'+firebase.auth().currentUser.uid).orderByValue().limitToLast(10).once('value', (dataSnapshot) => {
dataSnapshot.forEach((child) => {
firebaseApp.database().ref('messages').child(child.key).once("value", (message)=>{
const message = {
_id: Math.round(Math.random() * 1000000),
text: message.val().text,
createdAt: new Date(message.val().timestamp),
user: {
_id: 1,
name: 'Developer',
},
};
// Clone messages
messages = [...messages, message];
this.setState({ messages });
});
});
});
}

Related

Axios Spy not being called correct number of times in Jest

I have a React context I am testing that runs a single function to check for an application update. The checkForUpdate function looks like this:
async function checkForUpdate() {
if (isPlatform('capacitor')) {
const maintanenceURL =
'https://example.com/maintenance.json';
const updateURL =
'https://example.com/update.json';
try {
const maintanenceFetch: AxiosResponse<MaintanenceDataInterface> =
await axios.get(maintanenceURL);
console.log('maintain', maintanenceFetch);
if (maintanenceFetch.data.enabled) {
setUpdateMessage(maintanenceFetch.data.msg);
return;
}
const updateFetch: AxiosResponse<UpdateDataInterface> = await axios.get(
updateURL
);
console.log('updateFetch', updateFetch);
if (updateFetch.data.enabled) {
const capApp = await App.getInfo();
const capAppVersion = capApp.version;
console.log('Thi is a thinkg', capAppVersion);
if (isPlatform('android')) {
console.log('hi');
const { currentAndroid, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentAndroid.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
} else {
const { currentIos, majorMsg, minorMsg } = updateFetch.data;
const idealVersionArr = currentIos.split('.');
const actualVersionArr = capAppVersion.split('.');
if (idealVersionArr[0] !== actualVersionArr[0]) {
setUpdateMessage(majorMsg);
setUpdateAvailable(true);
return;
}
if (idealVersionArr[1] !== actualVersionArr[1]) {
setUpdateMessage(minorMsg);
setUpdateAvailable(true);
return;
}
}
}
} catch (err) {
console.log('Error in checkForUpdate', err);
}
}
}
For some reason, in my test I wrote to test this, my axiosSpy only shows that it has been called 1 time instead of the expected 2 times. The console logs I posted for both get requests run as well. I cannot figure out what I am doing wrong.
Here is the test:
it.only('should render the update page if the fetch call to update bucket is enabled and returns a different major version', async () => {
const isPlatformSpy = jest.spyOn(ionicReact, 'isPlatform');
isPlatformSpy.mockReturnValueOnce(true).mockReturnValueOnce(true);
const appSpy = jest.spyOn(App, 'getInfo');
appSpy.mockResolvedValueOnce({
version: '0.8.0',
name: 'test',
build: '123',
id: 'r132-132',
});
const axiosSpy = jest.spyOn(axios, 'get');
axiosSpy
.mockResolvedValueOnce({
data: {
enabled: false,
msg: {
title: 'App maintenance',
msg: 'We are currently solving an issue where users cannot open the app. This should be solved by end of day 12/31/2022! Thank you for your patience 😁',
btn: 'Ok',
type: 'maintenance',
},
},
})
.mockResolvedValueOnce({
data: {
current: '1.0.0',
currentAndroid: '1.0.0',
currentIos: '2.0.0',
enabled: true,
majorMsg: {
title: 'Important App update',
msg: 'Please update your app to the latest version to continue using it. If you are on iPhone, go to the app store and search MO Gas Tax Back to update your app. The button below does not work but will in the current update!',
btn: 'Download',
type: 'major',
},
minorMsg: {
title: 'App update available',
msg: "There's a new version available, would you like to get it now?",
btn: 'Download',
type: 'minor',
},
},
});
customRender(<UpdateChild />);
expect(axiosSpy).toHaveBeenCalledTimes(2);
});

i get two different values when i console.log the same array

I have a function that gets a product from my firstore database with the id.
when i console.log the array in the subscribe i get :
console.log(product[0]) -> Object { id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }
console.log(product) -> [{ id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }]
that's good and normal but when i do a console.log outside the subscribe.
Where i want to return the array i get different values.
then i get :
console.log(product[0]) -> undefined
console.log(product) -> [{ id: 0, quantity: 1, latinName: "stub", price: "10", name: "foo", }]
This is my code for getting the data and returning it
getProductfromDBByID(id: number): Observable<any> {
let product: Array<any> = [];
this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
product.push(doc.data());
console.log('product in subscribe ', product[0]);
});
});
console.log('product return value', product[0]);
return of(product);
}
That is because the console.log outside of the subscribe is executed before the subscribe is hit (asynchronous operation, request in this case).
To resolve that, move the actual subscription to the function that calls this function.
getProductfromDBByID(id: number): Observable<any> {
return this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get();
}
...
product: Array<any>;
otherFunction() {
getProductfromDBByID(/*some id*/)
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
this.product.push(doc.data());
});
});
}
Add "async" keyword before the function name and add "await" keyword before the future method which is the "this.firestore......"
async getProductfromDBByID(id: number): Observable<any> {
let product: Array<any> = [];
await this.firestore
.collection('products', (ref) => ref.where('id', '==', id))
.get()
.subscribe((querySnapshot) => {
querySnapshot.forEach((doc) => {
product.push(doc.data());
console.log('product in subscribe ', product[0]);
});
});
console.log('product return value', product[0]);
return of(product);
}

How to access data returning from an API Subscribe method outside in Angular

This is my angular code block.
demandCurveInfo = [];
ngOnInit() {
this.zone.runOutsideAngular(() => {
Promise.all([
import('#amcharts/amcharts4/core'),
import('#amcharts/amcharts4/charts'),
import('#amcharts/amcharts4/themes/animated'),
import('#amcharts/amcharts4/maps'),
import('#amcharts/amcharts4-geodata/worldLow'),
])
.then(modules => {
this.createDemandCurve(modules);
})
.catch(e => {
console.error('Error when creating chart', e);
});
});
}
This is where i am trying get API data.
async getDemandCurveInfo(statusType: string, valueType ) {
const params = [];
params.push({code: 'StreamCode', name: 'TG_PS'});
params.push({code: 'segmentCodes', name: ['US']});
params.push({code: 'checkinFrom', name: '2019-01-01'});
params.push({code: 'checkinTo', name: '2019-12-31'});
params.push({code: 'statusType', name: statusType});
params.push({code: 'valueType', name: valueType});
return await this.dashboardServiceHandler.getSegmentDemand([], params).toPromise();
}
Inside this function i am calling the above method.
createDemandCurve(modules: any) {
const am4core = modules[0];
const am4charts = modules[1];
const am4themesAnimated = modules[2].default;
this.getDemandCurveInfo('REAL', 'TTV').then((data) => {
this.demandCurveInfo.push(data.valueOf().data);
console.log(this.demandCurveInfo[0]); <- first
});
console.log(this.demandCurveInfo[0]); <- second
}
In here i am trying to get this.demandCurveInfo[0] data outside.But my second console.log gives output like undefined.First console.log gives output like this. How could i get the console.log data outside?.
You can introduce a new method to do that
this.getDemandCurveInfo('REAL', 'TTV').then((data) => {
this.demandCurveInfo.push(data.valueOf().data);
printData(this.demandCurveInfo[0]);
});
printData(data: string){
console.log(data);
}

How do I make my Discord bot change status every 10 seconds?

I have a smol Discord bot (with discord.js-commando), I have this code:
var activevar = ["with the &help command.", "with the developers console", "with some code", "with JavaScript"];
var activities = activevar[Math.floor(Math.random()*activevar.length)];
client.on('ready', () => {
client.user.setActivity(activities);
}
But that only changes it when I restart the bot. Can someone help me out here?
Edited for users on v12 which now uses bot instead of client
const activities = [
"with the &help command.",
"with the developers console.",
"with some code.",
"with JavaScript."
];
bot.on("ready", () => {
// run every 10 seconds
setInterval(() => {
// generate random number between 1 and list length.
const randomIndex = Math.floor(Math.random() * (activities.length - 1) + 1);
const newActivity = activities[randomIndex];
bot.user.setActivity(newActivity);
}, 10000);
});
I changed it so you can change the status from playing to watching or listening.
const activities_list = [
"For Rule Breakers",
"The purple names",
"#general",
"The mods do their job"
]; // creates an arraylist containing phrases you want your bot to switch through.
client.on('ready', () => {
setInterval(() => {
const index = Math.floor(Math.random() * (activities_list.length - 1) + 1); // generates a random number between 1 and the length of the activities array list (in this case 5).
client.user.setActivity(activities_list[index], { type: 'WATCHING' }); // sets bot's activities to one of the phrases in the arraylist.
}, 10000); // Runs this every 10 seconds.
});
Considering how often this is viewed, I thought that I'd provide an updated and clearer response.
const state = 0;
const presences = [
{ type: 'PLAYING', message: 'a game' },
{ type: 'WATCHING', message: 'a video' }
];
setInterval(() => {
state = (state + 1) % presences.length;
const presence = presences[state];
client.user.setActivity(presence.message, { type: presence.type });
}, 10000);
This will also work on v13 too.
const statuses = [
{ name: "to nothing", type: "LISTENING" },
{ name: "something", type: "PLAYING" },
{ name: "your commands", type: "WATCHING" },
];
client.on("ready", () => {
setInterval(() => {
var randomStatus = statuses[Math.floor(Math.random() * statuses.length)];
client.user.setActivity(randomStatus);
}, 10000);
});
Haven't tested, but it should work in theory. It not, try to figure out what is wrong, it's good practice. Otherwise, let me know
client.on("ready", function() {
setInterval(function() {
var actID = Math.floor(Math.random() * Math.floor(activevar.length));
client.user.setActivity(activities);
}, 10000)
});
changes every 10s for playing, watching, listening activities
const activities_list = [
{ type: 'PLAYING', message: 'a game' },
{ type: 'WATCHING', message: 'a video' },
{ type: 'LISTENING', message: 'a music' }
];
client.on('ready', () => {
setInterval(() => {
const index = Math.floor(Math.random() * (activities_list.length - 1) + 1);
client.user.setActivity(activities_list[index].message, { type: activities_list[index].type });
}, 10000);
});
i'm use discord.js v12.
client.on("ready", () => {
console.log(`ok`);
const who = ["hi", "hello"];
setInterval(() => {
const burh = Math.floor(Math.random() * who.length);
client.user.setPresence({ activity: { name: who[burh]}, status: 'dnd'});
}, 5000);
});

Mongoose/Mongo: Update Not Saving

I'm extremely perplexed by this issue that I'm having with mongo/mongoose. I'm essentially trying to get an array of products, delete a certain product from the array, and then update the shopping chart with the new array that omits the selected product. Here's the snippet of code I'm dealing with:
const remove = (req, res, next) => {
console.log('here is the product id ' + req.body.cart.product)
delete req.body._owner // disallow owner reassignment.
Cart.find({_id: req.user.cartId})
.then((products1) => {
console.log("array of products: " + products1[0].product)
const index = products1[0].product.indexOf(req.body.cart.product)
console.log("index valeu: " + index)
if (index > -1) {
products1[0].product.splice(index, 1)
return products1[0].product
}
return products1[0].product
})
.then((products2) => {
console.log('Second Promise Input: ' + products2)
Cart.update({_id: req.user.cartId}, {$set: {product: products2}})
})
.then(() => res.sendStatus(204))
.catch(next)
}
And here's the output from my server:
Server listening on port 4741
here is the product id 5952b57ea52d092b8d34c6b0
array of products: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
index valeu: 0
Second Promise Input: 5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0,5952b57ea52d092b8d34c6b0
PATCH /carts-decrease/595b037e128cfd37e0c864d7 204 38.773 ms
According to my console.logs, I'm getting the array just the way I want it but it simply does not update the shopping cart with the new array. I've been staring at this code for far too long and I'd appreciate a second set of eyes on this. Thanks.
P.S. Ignore the fact that the product ids are all the same, its just a testing variable
Cart Schema:
'use strict'
const mongoose = require('mongoose')
const cartSchema = new mongoose.Schema({
product: {
type: Array,
required: false
},
owner: {
type: mongoose.Schema.Types.ObjectId,
ref: 'User',
required: false
}
}, {
timestamps: true,
toJSON: {
virtuals: true,
transform: function (doc, ret, options) {
const userId = (options.user && options.user._id) || false
ret.editable = userId && userId.equals(doc._owner)
return ret
}
}
})
const Cart = mongoose.model('Cart', cartSchema)
module.exports = Cart
Product Schema:
'use strict'
const mongoose = require('mongoose')
const productSchema = new mongoose.Schema({
name: {
type: String,
required: true
},
price: {
type: Number,
required: true
},
description: {
type: String,
required: true
}
}, {
toJSON: {
virtuals: true
}
})
const Product = mongoose.model('Product', productSchema)
module.exports = Product
Show request:
const show = (req, res) => {
const product = {}
product.array = []
// console.log(req.cart.product)
const promises = []
Promise.all(req.cart.product.map(function (id) {
return Product.find({_id: ObjectId(id)})
})).then(function (products) {
console.log(products)
req.cart.product = products
return res.json({
cart: req.cart.toJSON({virtuals: true, user: req.user})
})
}).catch(function (err) {
console.log(err)
return res.sendStatus(500)
})
}
I would recommend you to slightly modify your cartSchema and store products in the form of an array of embedded documents:
const cartSchema = new mongoose.Schema({
products: [{
name: { type: String },
price: { type: Number }
...
}]
...
});
If you do this you can simply use the $pull update operator to remove products from your cart:
{ $pull: { <field1>: <value|condition>, <field2>: <value|condition>, ... } }
In your case the query should then look like this:
Cart.update(
{ _id: req.user.cartId },
{ $pull: { products: { '_id': req.body.cart.product } }}
);
As the embedded documents will have their own ObjectId there will only be one document matching the query.

Resources