The problem is that every time I click on an element with a state things appear twice. For example if i click on a button and the result of clicking would be to output something in the console, it would output 2 times. However in this case, whenever I click a function is executed twice.
The code:
const getfiles = async () => {
let a = await documentSpecifics;
for(let i = 0; i < a.length; i++) {
var wrt = document.querySelectorAll("#writeto");
var fd = document.querySelector('.filtered-docs');
var newResultEl = document.createElement('div');
var writeToEl = document.createElement('p');
newResultEl.classList.add("result");
writeToEl.id = "writeto";
newResultEl.appendChild(writeToEl);
fd.appendChild(newResultEl);
listOfNodes.push(writeToEl);
listOfContainers.push(newResultEl);
wrt[i].textContent = a[i].data.documentName;
}
}
The code here is supposed to create a new div element with a paragraph tag and getting data from firebase firestore, will write to the p tag the data. Now if there are for example 9 documents in firestore and i click a button then 9 more divs will be replicated. Now in total there are 18 divs and only 9 containing actual data while the rest are just blank. It continues to create 9 more divs every click.
I'm also aware of React.Strictmode doing this for some debugging but I made sure to take it out and still got the same results.
Firebase code:
//put data in firebase
createFileToDb = () => {
var docName = document.getElementById("title-custom").value; //get values
var specifiedWidth = document.getElementById("doc-width").value;
var specifiedHeight = document.getElementById("doc-height").value;
var colorType = document.getElementById("select-color").value;
parseInt(specifiedWidth); //transform strings to integers
parseInt(specifiedHeight);
firebase.firestore().collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.add({
documentName: docName,
width: Number(specifiedWidth), //firebase-firestore method for converting the type of value in the firestore databse
height: Number(specifiedHeight),
docColorType: colorType,
creation: firebase.firestore.FieldValue.serverTimestamp() // it is possible that this is necessary in order to use "orderBy" when getting data
}).then(() => {
console.log("file in database");
}).catch(() => {
console.log("failed");
})
}
//get data
GetData = () => {
return firebase.firestore()
.collection("documents")
.doc(firebase.auth().currentUser.uid)
.collection("userDocs")
.orderBy("creation", "asc")
.get()
.then((doc) => {
let custom = doc.docs.map((document) => {
var data = document.data();
var id = document.id;
return { id, data }
})
return custom;
}).catch((err) => {console.error(err)});
}
waitForData = async () => {
let result = await this.GetData();
return result;
}
//in render
let documentSpecifics = this.waitForData().then((response) => response)
.then((u) => {
if(u.length > 0) {
for(let i = 0; i < u.length; i++) {
try {
//
} catch(error) {
console.log(error);
}
}
}
return u;
});
Edit: firebase auth is functioning fine so i dont think it has anything to do with the problem
Edit: This is all in a class component
Edit: Clicking a button calls the function createFileToDb
I think that i found the answer to my problem.
Basically, since this is a class component I took things out of the render and put some console.log statements to see what was happening. what i noticed is that it logs twice in render but not outside of it. So i took the functions out.
Here is the code that seems to fix my issue:
contain = () => {
const documentSpecifics = this.waitForData().then((response) => {
var wrt = document.getElementsByClassName('writeto');
for(let i = 0; i < response.length; i++) {
this.setNewFile();
wrt[i].textContent = response[i].data.documentName;
}
return response;
})
this.setState({
docs: documentSpecifics,
docDisplayType: !this.state.docDisplayType
})
}
As for creating elements i put them in a function so i coud reuse it:
setNewFile = () => {
const wrt = document.querySelector(".writeto");
const fd = document.querySelector("#filtered-docs");
var newResultEl = document.createElement('div');
newResultEl.classList.add("result");
var wrtEl = document.createElement('p');
wrtEl.classList.add("writeto");
fd.appendChild(newResultEl);
newResultEl.appendChild(wrtEl);
}
The firebase and firestore code remains the same.
the functions are called through elements in the return using onClick.
I am trying to do a transaction that loops through multiple items and inserts them into the database. If i just have 1 item in each array, the code executes fine, it inserts the deployment, the crew and the equipment. However if I have 2 or more items in either equipment or crew, the application just freezes, no error or anything.
The console output looks like this:
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): START TRANSACTION;
----------------------CREATE DEPLOYMENT---------------
Executing (e337b7de-e95f-4d18-a2e9-1216cb8b7d61): INSERT INTO `deployments` (`id
`,`dateDeployed`,`dateReturned`,`city`,`province`,`country`,`fireName`,`fireNumb
er`,`fireCenter`,`unitNumber`,`comments`,`finalSold`,`status`,`createdAt`,`updat
edAt`,`contractId`,`deploymentTypeId`,`productId`) VALUES (DEFAULT,'2018-03-01',
'Invalid date','1','BC','CAN','1','1','1','1','test','','active','2018-03-08 22:
36:44','2018-03-08 22:36:44','5','1','1');
----------------------CREATE EQUIPEMENT---------------
----------------------CREATE EQUIPEMENT---------------
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:31','2018-03-08 22:36:4
4',17,1);
Executing (default): INSERT INTO `deploymentEquipments` (`createdAt`,`updatedAt`
,`deploymentId`,`equipmentId`) VALUES ('2018-03-08 18:09:39','2018-03-08 22:36:4
4',17,2);
My code is like this:
app.post('/deployment', function(req,res,next){
var deployment = req.body;
var crew = req.body.deploymentCrew;
var equipment = req.body.deploymentEquipment;
var deploymentId = "";
//insert new deployment - start transaction, add deployment, get ID, loop through crew, loop through equipment
models.sequelize.transaction(t =>
{
var equipPromises = [];
console.log('----------------------CREATE DEPLOYMENT---------------');
return models.deployment.create(req.body, {transaction: t})
.then(function(newDeployment) {
deploymentId = newDeployment.dataValues.id;
for (var i = 0; i < equipment.length; i++) {
console.log('----------------------CREATE EQUIPEMENT---------------');
var equip = equipment[i];
equip.deploymentId = deploymentId;
equip.equipmentId = equipment[i].id;
var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
}
return Promise.all(equipPromises);
})
.then(function() {
console.log('----------------------CREATE STAFF---------------');
var crewPromises = [];
for (var i = 0; i < crew.length; i++) {
var staff = crew[i];
staff.deploymentId = deploymentId;
staff.staffId = crew[i].staff.id;
var crewPromise = models.deploymentCrew.create(staff, crewPromises.push(crewPromise));
}
return Promise.all(crewPromises);
});
}).then(result => {
console.log('deployment added');
res.send(result);
}).catch(err => {
console.log('deployment creation failed');
res.status(401).send({'message':err, 'redirect': '/'});
});
});
Any thought's or ideas why this might be happening would be appreciated.
Thanks
It was actually quite simple, I wasn't adding the transaction into the looped create statements. So now it is like:
//this is the old statement
//var equipPromise = models.deploymentEquipment.create(equip, equipPromises.push(equipPromise));
//this is the new correct way to do it
equipPromises.push(models.deploymentEquipment.create(equip, {transaction:t}));
Parse-server doesn't support groupBy for queries. So instead of adapting code to work with the duplicate entries i've decided to create a Job to clean the data.
I've created a cloud function using underscore but the results are not good. It's deleting non-duplicate entries too.
I want to remove a entry if another entry exists with the same post_id and user_id
Parse.Cloud.job("removeDuplicateItems", function(request, status) {
var _ = require("underscore");
var hashTable = {};
function hashKeyForTestItem(testItem) {
var fields = ["user_id", "post_id"];
var hashKey = "";
_.each(fields, function (field) {
hashKey += testItem.get(field) + "/" ;
});
return hashKey;
}
var testItemsQuery = new Parse.Query("Post_shares");
testItemsQuery.each(function (testItem) {
var key = hashKeyForTestItem(testItem);
if (key in hashTable) { // this item was seen before, so destroy this
return testItem.destroy();
} else { // it is not in the hashTable, so keep it
hashTable[key] = 1;
}
}).then(function() {
status.success("removal completed successfully.");
}, function(error) {
status.error("Uh oh, something went wrong.");
});
});
Is there a better way of doing this?
I have a list of company and would like to calculate a total amount of invoices issued to each company. The following is the code that I wrote. (Actual logic is more complicated within the loop but took them out here)
Basically I want to alert the message once the business logic within the loop is complete (Again, it will do something more complex here). I got a feeling that I can resolve this issue by using Promises but am not quite sure how to use it. I didn't quite follow Parse.com's document. I have been stuck with this for a few hours. Please help!
function calculate(companies) {
companies.forEach(function(company) {
var total = 0;
var invoice = Parse.Object.extend('Invoice');
var query = new Parse.Query(invoice);
query.equalTo('invoiceCompany', company);
query.find().then(function(invoices) {
invoices.forEach(function(invoice) {
total += parseFloat(invoice.get('amount'));
});
});
});
alert("Calculated Finished");
}
You can use promises in paralell:
https://parse.com/docs/js/guide#promises-promises-in-parallel
It would be something like this:
function calculate(companies) {
var promises = [];
companies.forEach(function(company) {
var total = 0;
var invoice = Parse.Object.extend('Invoice');
var query = new Parse.Query(invoice);
query.equalTo('invoiceCompany', company);
var queryPromise = query.find().then(function(invoices) {
invoices.forEach(function(invoice) {
total += parseFloat(invoice.get('amount'));
});
});
promises.push(queryPromise);
});
return Parse.Promise.when(promises);
}
calculate(companies).then(function() {
alert("Calculated Finished");
});
I am new to Mongo and Node. I am currently using Mongoskin and Bluebird to handle the db connection and queries (as suggested here: https://stackoverflow.com/a/23687958/2701348 ).
I have three collections: Users, Binders and Cards.
The Binders collection contains the information about Cards for each User.
Each document in Binders has as properties:
User Id <--- that refers to the User owning the Card
Card Code <--- that refers to a Card
Count <--- that refers to the number of cards owned by the User
I prefer to have a separate Cards collection so that when a Card changes, it changes for all the Users Binders.
Now I am willing to retrieve an array for a given user such as:
[{card: {card document}, count: 4}, ...]
I have the following problems:
the db connection should be closed after all the async db callbacks are called
the cards array should be returned after the last db.collection('cards').find gives back the results
I know my following code is wrong but can be a starting point for a discussion:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var promiseCards;
//console.log("------ binderCards: ", binderCards);
var cards = [];
for (var i = 0; i < binderCards.length; i++) {
var binderCard = binderCards[i];
promiseCards = db.collection('cards').find({Code: binderCard.Code}).toArrayAsync();
promiseCards.then(function(cardsDB){
if(cardsDB){
//console.log("Cards found: ",binderCard.Code, cardsDB);
for (var i = 0; i < cardsDB.length; i++) {
cardsDB[i].count = binderCard.count;
};
cards.concat(cardsDB);
}
});
}
promiseCards.then(function(){
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
I am struggling trying to figure out how to handle the promisfied asynchronous call correctly in order to send back the whole array and close the db connection.
I think I should try to build a promise before the for loop and use it to chain the query on Cards promises and lastly chain the db.close() and res.json(cards) statements.
[EDIT] Maybe the easiest solution is to simply use the $in filter inside a single db.collection('cards').find({Code: {$in: [bindersCodeArray] }}).toArrayAsync(); and avoid that for loop:
var getAllBinderCards = function(req, res){
var db = req.db;
var userId = req.userId;
var promiseBinders = db.collection('binders').find({userId: userId}).toArrayAsync();
promiseBinders.then(function(binderCards) {
if (binderCards) {
var binderCodes = binderCards.map(function(element){
return element.Code;
});
var promiseCards = db.collection('cards').find({Code: {$in: binderCodes} }).toArrayAsync();
promiseCards.then(function(cards){
var bindersDictionary = {};
for (var i = 0; i < binderCards.length; i++) {
bindersDictionary[binderCards[i].Code] = binderCards[i].count;
};
for (var i = 0; i < cards.length; i++) {
cards[i].count = bindersDictionary[cards[i].Code];
};
db.close();
console.log("Binder Cards: " , cards);
res.json(cards);
});
}
});
}
Still I am curious if there is an elegant way to solve this riddle using promises.
I would expect that using $in and array may have constraints on the number of binders you can pass and affect query performance. You can also try doing this with async#map. e.g.:
...
function(binders) {
async.map(binders, cardsForBinders, function(err, bindersWithCards) {
// TODO: close connection here.
}
function cardsForBinders(binder, callback) {
// 1. find cards for binder.
// 2. prepare transformed response: binderWithCards.
callback(null, binderWithCards);
}
}
...