How to access a local variable's value in this situation? - javascript

I'm trying to read the number of times an event has been logged and increment/decrement a variable accordingly. Since I can't use this.setState inside the getPastEvent (because it generates an Unhandled Runtime Error which is TypeError: Cannot read property 'setState' of undefined), I'm opting for this method where I perform my counting on a local variable then save it to the state variable.
The issue here is when I use this.setState({totalBidders: biddersnumber}); at the end of the function, I receive the value zero where in my case it should be two! How can I get the value of the counter biddersnumber in this situation?
componentDidMount = async () => {
const accounts = await web3.eth.getAccounts();
const plasticBaleSC = plasticBaleContract(this.props.address);
var biddersnumber = 0;
var highestbid = 0;
plasticBaleSC.getPastEvents('allEvents', { fromBlock: 0, toBlock: 'latest' }, function (error, events) {
console.log(events);
events.forEach(myfunction);
function myfunction(item, index) {
if (item.event === 'bidderRegistered') {
console.log(item);
biddersnumber++;
//value is two here
console.log(biddersnumber);
} else if (item.event === 'bidPlaced') {
} else if (item.event === 'bidderRegistered') {
} else if (item.event === 'bidderExited') {
console.log(item);
biddersnumber--;
} else if (item.event === 'auctionStarted') {
}
}
});
//Value is zero here
this.setState({ totalBidders: biddersnumber });
}

Using comments on this question, I did the following updates to resolve the issue. Thank you Andrea!
componentDidMount = async () => {
const accounts = await web3.eth.getAccounts();
const plasticBaleSC = plasticBaleContract(this.props.address);
var biddersnumber = 0;
var highestbid =0;
plasticBaleSC.getPastEvents("allEvents",{fromBlock: 0, toBlock:'latest'},(error, events)=>{
console.log(events);
const myfunction = (item,index) => {
if(item.event==='bidderRegistered'){
console.log(item);
biddersnumber++;
console.log(biddersnumber);
}else if(item.event==='bidPlaced'){
}else if(item.event==='bidderRegistered'){
}else if (item.event==='bidderExited'){
console.log(item);
biddersnumber--;
}else if(item.event==='auctionStarted'){
//console.log(item);
//this.setState({highestBid: item.returnValues['startingAmount']});
}
};
events.forEach(myfunction);
this.setState({totalBidders: biddersnumber});
});
};

You need to put the call to setState inside your callback:
// ....
var highestbid =0;
const that = this
// ....
// Value is two here
that.setState({totalBidders: biddersnumber});
});
//Value is zero here
};
You'll need to deal with this, so assign it to that like it's 2010.

Related

Jest testing, keeps return undefined?

I'm trying to test a function with jest, and I simply can figure out what's wrong? It keeps saying it expects to return the output, but got undefined. I have tested the function elsewhere where it seems to return the correct array.
I'm calling my my function and passing it an Object, it's then supposed to return an array. Then I'm calling .toEqual(output) which is an array.
//This is my function
const allAddresses = [
];
const updateAllAddresses = (obj) => {
const transferAmount = obj.transferAmount;
const to = obj.to;
const transferAddress = obj.address;
const newBalance = obj.newBalance;
const addressArr = [...allAddresses];
console.log("This addressArr", addressArr);
console.log("this is obj", obj);
//To set your account to the new balance after transfer and
//to check if the address you transfer to is your own account
addressArr.map((address) => {
if (address.account === transferAddress) {
console.log("This is inside the map !!!!");
address.balance = Number(newBalance);
}
if (address.account === to) {
console.log("2");
address.balance = Number(transferAmount) + Number(address.balance);
}
console.log("last part of the testing", addressArr);
return addressArr;
});
};
const obj = {
};
const output = [
];
//This is my test
describe("Update array", () => {
test("update the array with the new information", () => {
expect(updateAllAddresses(obj)).toEqual(output);
});
});
You cannot short circuit and return inside a map function. You should return the object after the map
Also, when you change address inside the map; It really does not change anything, since that address variable will be removed from memory on next iteration
There is a problem with your updateAllAddresses method.
You are not returning anything then the result of your function becomes undefined;
add return to where you are using .map method.
return addressArr.map((address) => {
if (address.account === transferAddress) {
console.log("This is inside the map !!!!");
address.balance = Number(newBalance);
}
if (address.account === to) {
console.log("2");
address.balance = Number(transferAmount) + Number(address.balance);
}
console.log("last part of the testing", addressArr);
return address;
});

Wait for all Firebase data query requests before executing code

I am trying to fetch data from different collections in my cloud Firestore database in advance before I process them and apply them to batch, I created two async functions, one to capture the data and another to execute certain code only after all data is collected, I didn't want the code executing and creating errors before the data is fetched when i try to access the matchesObject after the async function to collect data is finished, it keeps saying "it cannot access a property matchStatus of undefined", i thought took care of that with async and await? could anyone shed some light as to why it is undefined one moment
axios.request(options).then(function(response) {
console.log('Total matches count :' + response.data.matches.length);
const data = response.data;
var matchesSnapshot;
var marketsSnapshot;
var tradesSnapshot;
var betsSnapshot;
matchesObject = {};
marketsObject = {};
tradesObject = {};
betsObject = {};
start();
async function checkDatabase() {
matchesSnapshot = await db.collection('matches').get();
matchesSnapshot.forEach(doc => {
matchesObject[doc.id] = doc.data();
console.log('matches object: ' + doc.id.toString())
});
marketsSnapshot = await db.collection('markets').get();
marketsSnapshot.forEach(doc2 => {
marketsObject[doc2.id] = doc2.data();
console.log('markets object: ' + doc2.id.toString())
});
tradesSnapshot = await db.collection('trades').get();
tradesSnapshot.forEach(doc3 => {
tradesObject[doc3.id] = doc3.data();
console.log('trades object: ' + doc3.id.toString())
});
betsSnapshot = await db.collection('bets').get();
betsSnapshot.forEach(doc4 => {
betsObject[doc4.id] = doc4.data();
console.log('bets object: ' + doc4.id.toString())
});
}
async function start() {
await checkDatabase();
// this is the part which is undefined, it keeps saying it cant access property matchStatus of undefined
console.log('here is matches object ' + matchesObject['302283']['matchStatus']);
if (Object.keys(matchesObject).length != 0) {
for (let bets of Object.keys(betsObject)) {
if (matchesObject[betsObject[bets]['tradeMatchId']]['matchStatus'] == 'IN_PLAY' && betsObject[bets]['matched'] == false) {
var sfRef = db.collection('users').doc(betsObject[bets]['user']);
batch11.set(sfRef, {
accountBalance: admin.firestore.FieldValue + parseFloat(betsObject[bets]['stake']),
}, {
merge: true
});
var sfRef = db.collection('bets').doc(bets);
batch12.set(sfRef, {
tradeCancelled: true,
}, {
merge: true
});
}
}
}
});
There are too many smaller issues in the current code to try to debug them one-by-one, so this refactor introduces various tests against your data. It currently won't make any changes to your database and is meant to be a replacement for your start() function.
One of the main differences against your current code is that it doesn't unnecessarily download 4 collections worth of documents (two of them aren't even used in the code you've included).
Steps
First, it will get all the bet documents that have matched == false. From these documents, it will check if they have any syntax errors and report them to the console. For each valid bet document, the ID of it's linked match document will be grabbed so we can then fetch all the match documents we actually need. Then we queue up the changes to the user's balance and the bet's document. Finally we report about any changes to be done and commit them (once you uncomment the line).
Code
Note: fetchDocumentById() is defined in this gist. Its a helper function to allow someCollectionRef.where(FieldPath.documentId(), 'in', arrayOfIds) to take more than 10 IDs at once.
async function applyBalanceChanges() {
const betsCollectionRef = db.collection('bets');
const matchesCollectionRef = db.collection('matches');
const usersCollectionRef = db.collection('users');
const betDataMap = {}; // Record<string, BetData>
await betsCollectionRef
.where('matched', '==', false)
.get()
.then((betsSnapshot) => {
betsSnapshot.forEach(betDoc => {
betDataMap[betDoc.id] = betDoc.data();
});
});
const matchDataMap = {}; // Record<string, MatchData | undefined>
// betIdList contains all IDs that will be processed
const betIdList = Object.keys(betDataMap).filter(betId => {
const betData = betDataMap[betId];
if (!betData) {
console.log(`WARN: Skipped Bet #${betId} because it was falsy (actual value: ${betData})`);
return false;
}
const matchId = betData.tradeMatchId;
if (!matchId) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy match ID (actual value: ${matchId})`);
return false;
}
if (!betData.user) {
console.log(`WARN: Skipped Bet #${betId} because it had a falsy user ID (actual value: ${userId})`);
return false;
}
const stakeAsNumber = Number(betData.stake); // not using parseFloat as it's too lax
if (isNaN(stakeAsNumber)) {
console.log(`WARN: Skipped Bet #${betId} because it had an invalid stake value (original NaN value: ${betData.stake})`);
return false;
}
matchDataMap[matchId] = undefined; // using undefined because its the result of `doc.data()` when the document doesn't exist
return true;
});
await fetchDocumentsById(
matchesCollectionRef,
Object.keys(matchIdMap),
(matchDoc) => matchDataMap[matchDoc.id] = matchDoc.data()
);
const batch = db.batch();
const queuedUpdates = 0;
betIdList.forEach(betId => {
const betData = betDataMap[betId];
const matchData = matchDataMap[betData.tradeMatchId];
if (matchData === undefined) {
console.log(`WARN: Skipped /bets/${betId}, because it's linked match doesn't exist!`);
continue;
}
if (matchData.matchStatus !== 'IN_PLAY') {
console.log(`INFO: Skipped /bets/${betId}, because it's linked match status is not "IN_PLAY" (actual value: ${matchData.matchStatus})`);
continue;
}
const betRef = betsCollectionRef.doc(betId);
const betUserRef = usersCollectionRef.doc(betData.user);
batch.update(betUserRef, { accountBalance: admin.firestore.FieldValue.increment(Number(betData.stake)) });
batch.update(betRef, { tradeCancelled: true });
queuedUpdates += 2; // for logging
});
console.log(`INFO: Batch currently has ${queuedUpdates} queued`);
// only uncomment when you are ready to make changes
// batch.commit();
}
Usage:
axios.request(options)
.then(function(response) {
const data = response.data;
console.log('INFO: Total matches count from API:' + data.matches.length);
return applyBalanceChanges();
}

React Hook returning base values in useEffect

I ran into a roadblock when trying to update a hook when the web socket is called with new information and noticed that the hooks are returning the default values I set them to inside my useEffect, whilst inside the render it is returning the correct values. I am completely stumped and unsure why and was curious as to if anyone could help, much appreciated.
const [view, setView] = useState(false)
const [curFlip, setFlip] = useState(null)
tradeSocket.addEventListener('message', async (msg) => {
const message = JSON.parse(msg.data)
if (message.tradelink) {
// not needed
} else if (message.redItems || message.blueItems) {
// not needed
} else if (message.flips) {
console.log('effect ', view, curFlip) // this is where the issue occurs, it returns false and null
if (view && curFlip) {
console.log('theyre viewing a flip')
for (let i = 0; i < message.flips.length; i++) {
console.log('looping ' + i, message.flips[i].offer)
if (message.flips[i].offer === curFlip.offer) {
setFlip(message.flips[i])
}
}
}
setCoinflips(message.flips)
} else if (message.tradeid) {
// not needed
}
})
Image of what values it returns per render / effect called.
Based on our output, it seems that you set up the socket listener only once on initial render in useEffect.
Now since the useEffect callback is run once, the values used from closure inside the listener function will always show the initial valued
The solution here is to add view and curFlip to dependency array of useEffect and close the socket in useEffect cleanup function
useEffect(() => {
tradeSocket.addEventListener('message', async (msg) => {
const message = JSON.parse(msg.data)
if (message.tradelink) {
// not needed
} else if (message.redItems || message.blueItems) {
// not needed
} else if (message.flips) {
console.log('effect ', view, curFlip) // this is where the issue occurs, it returns false and null
if (view && curFlip) {
console.log('theyre viewing a flip')
for (let i = 0; i < message.flips.length; i++) {
console.log('looping ' + i, message.flips[i].offer)
if (message.flips[i].offer === curFlip.offer) {
setFlip(message.flips[i])
}
}
}
setCoinflips(message.flips)
} else if (message.tradeid) {
// not needed
}
})
return () => {
tradeSocket.close();
}
}
}, [curFlip, view]);

How to break the for loop using state

I have code as below.
I need to break the loop when first match is found.
const [isCodeValid, setIsCodeValid] = useState(false);
for (let i = 0; i < properyIds.length; i++) {
if (isCodeValid) {
break; // this breaks it but had to click twice so state would update
}
if (!isCodeValid) {
firestore().collection(`properties`)
.doc(`${properyIds[i]}`)
.collection('companies').get()
.then(companies => {
companies.forEach(company => {
if (_.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())) {
console.log("should break here")
// updating state like this wont take effect right away
// it shows true on second time click. so user need to click twice right now.
setIsCodeValid(true);
}
});
})
}
}
state won't update right away so if (!isCodeValid) only works on second click.
Once I find match I need to update state or variable so I can break the for loop.
I tried to use a variable but its value also not changing in final if condition, I wonder what is the reason? can anyone please explain ?
You should try and rewrite your code such that you will always call setIsCodeValid(value) once. In your case it could be called multiple times and it might not get called at all
const [isCodeValid, setIsCodeValid] = useState(false);
function checkForValidCode() {
// map to an array of promises for companies[]
const companiesPromises = properyIds.map(propertyId =>
firestore()
.collection(`properties`)
.doc(propertyId)
.collection('companies').get())
Promise.all(companiesPromises)
// flatten the 2d array to single array, re-create to JS array because of firestores internal types?
.then(companiesArray => [...companiesArray].flatMap(v => v))
// go through all companies to find a match
.then(companies =>
companies.find(
company => _.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())
))
.then(foundCompany => {
// code is valid if we found a matching company
setIsCodeValue(foundCompany !== undefined)
})
}
Try something like this:
import { useState } from 'react';
function YourComponent({ properyIds }) {
const [isCodeValid, setIsCodeValid] = useState(false);
async function handleSignupClick() {
if (isCodeValid) {
return;
}
for (let i = 0; i < properyIds.length; i++) {
const companies = await firestore()
.collection(`properties`)
.doc(`${properyIds[i]}`)
.collection('companies')
.get();
for (const company of companies.docs) {
if (_.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())) {
setIsCodeValid(true);
return;
}
}
}
}
return (<button onClick={handleSignupClick}>Sign Up</button>);
}
If you await these checks, that will allow you to sequentially loop and break out with a simple return, something you can't do inside of a callback. Note that if this is doing database queries, you should probably show waiting feedback while this is taking place so the user knows that clicking did something.
Update:
You may want to do all these checks in parallel if feasible so the user doesn't have to wait. Depends on your situation. Here's how you'd do that.
async function handleSignupClick() {
if (isCodeValid) {
return;
}
const allCompanies = await Promise.all(
properyIds.map(id => firestore()
.collection(`properties`)
.doc(`${properyIds[i]}`)
.collection('companies')
.get()
)
);
setIsCodeValid(
allCompanies.some(companiesSnapshot =>
companiesSnapshot.docs.some(company =>
_.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())
)
)
);
}
Can you not break it after setIsCodeValid(true);?
Use some:
companies.some(company => {
return _.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase());
});
If some and forEach are not available then companies is not an array but an array-like object. To iterate through those, we can use for of loop:
for (const company of companies){
if (_.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())) {
// do something
break;
}
}
I tired below and it worked for me to break the loop.
I declared and tried to change this variable let codeValid and it was just not updating its value when match found. (not sure why)
But all of a sudden I tried and it just works.
I didnt change any actual code except for variable.
let codeValid = false;
let userInformation = []
for (let i = 0; i < properties.length; i++) {
console.log("called")
const companies = await firestore().collection(`properties`)
.doc(`${properties[i].id}`)
.collection('companies').get()
.then(companies => {
companies.forEach(company => {
if (_.trim(company.data().registrationCode) === _.trim(registrationCode.toUpperCase())) {
// a += 1;
codeValid = true;
userInformation.registrationCode = registrationCode.toUpperCase();
userInformation.companyName = company.data().companyName;
userInformation.propertyName = properties[i].propertyName;
}
});
})
if (codeValid) {
break;
}
}

react native cant update state array in function

Im trying to push object into a state array in a function.But i cant, when i debug my array it is still empty..this.state = {
searchArray : [],}
here is my function
doSearch = (textinput) => {
let arrSearch = this.state.searchArray
//iterate model object
this.state.models[this.props.sexeModel].map(
(model) => {
let idmodel = model.id
//check if typed text is included in model name
if(model.nom.includes(textinput.SearchText)){
if(textinput.SearchText.length !== 0) {
//Checking if Search arr is empty
if(arrSearch.length === 0) {
let stateArray = this.state.searchArray
let joined = stateArray.concat(model)
this.setState({ searchArray: joined }) // CANT UPDATE HERE
} else {
arrSearch.map((modelSearch) => {
if(modelSearch.id == idmodel) {
console.log("Do not insert")
} else {
console.log("Insert")
let joined = arrSearch.concat(model)
this.setState({ arrSearch: joined })// CANTE UPDATE HERE
}
})
}
} else {
console.log("include but empty")
this.setState({searchArray:[]}) //CANT UPDATE HERE
}
} else {
console.log("not included")
this.setState({searchArray:[]}) // CANT UPDATE HERE
}
}
)
}
I can update a basic string or int/float state value in this function but not an array.Why ?
Any Ideas ?

Categories

Resources