ReactJS useState not updating momentJS prop - javascript

I am coding a CRM app and there's a asnyc function for gettingTrackers and it working well. There's another function called calculateStartTime and this function suppose to calculate momentJS variable and set it but this is not updating.
useEffect(() => {
async function gettingTrackers() {
await getTrackers(null, null);
}
gettingTrackers();
calculateStartTime();
}, []);
const [startTime, setStartTime] = useState(moment().format("YYYY-MM-DD"));
const [endTime, setEndTime] = useState(moment().format("YYYY-MM-DD"));
const calculateStartTime = () => {
const dateOfMay = moment("2020-05-01");
const now = moment();
let timeTheStart = null;
let timeTheEnd = null;
if (now.add(-32, "d").diff(dateOfMay) <= 0) {
timeTheStart = dateOfMay.format("YYYY-MM-DD")
timeTheEnd = moment().add(-2, "d").format("YYYY-MM-DD");
} else {
timeTheStart = moment().add(-32, "d").format("YYYY-MM-DD");
timeTheEnd = moment().add(-2, "d").format("YYYY-MM-DD");
}
console.log("calculating...")
console.log("start time > ", timeTheStart)
console.log("end time > ", timeTheEnd);
setStartTime(moment(timeTheStart).format("YYYY-MM-DD"))
setEndTime(moment(timeTheEnd).format("YYYY-MM-DD"))
// these 2 logs prints initial value, not updated value.
console.log(startTime);
console.log(endTime)
}
The problem is that I have to send startTime and endTime to another ReactJS component, and it sends first initial today value every time. When I call calculateStartTime it logs
calculating...
start time > 2020-06-07
end time > 2020-07-07
But when I click to button for another component, I print these variables and its output;
2020-07-09
2020-07-09
as initial values. I log them with using startTime and endTime as I described in useState
What I am missing on this problem? Is there any memory-leak to not-working?
Edit:
const goToResultButton = (event, data) => {
event.preventDefault();
console.log("start time > ", startTime)
console.log("end time > ", endTime)
}
Thanks in advance!

With the below code block, you are setting the state and immediately you are trying to access the updated value, but state updates done in async fashion. You will get the latest value in the next re-render.
...
setStartTime(moment(timeTheStart).format("YYYY-MM-DD"))
setEndTime(moment(timeTheEnd).format("YYYY-MM-DD"))
// these 2 logs prints initial value, not updated value.
console.log(startTime);
console.log(endTime)
...
You can use a useEffect to log or do something with latest values of startTime and endTime.
useEffect(() => {
console.log("startTime", startTime);
console.log("endTime", endTime);
}, [startTime, endTime]);

Related

How do I update a state each second for next n seconds

I have a state variable resendTime and magicLinkState.
I would like to setResendTime to resendTime - 1 for next 10 seconds when magicLinkState becomes sent.
I have tried the following code but this does not update resendTime every second.
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
const timer = setInterval(() => setResendTime(resendTime - 1), 1000);
console.log(resendTime);
return () => clearInterval(timer);
}
}, []);
This code only updates the state for the first second. I have also tried passing dependency array but didn't worked.
How do I make it update the state so I achieve what is intended.
The general idea is correct in your code but needs some adjustments.
it's required to add the magicLinkState to the effect's dependencies otherwise it won't trigger when this gets the sent value but only if it's initially set with this value.
you should use the arrow syntax: setResendTime((prevResendTime) => prevResendTime - 1) in order to grab the correct state value each time
there is no need for a cleanup function in this effect since you want to clear the interval only after it's triggered and made 10 decreases to resendTime.
you should add some local count variable in order for the decrease to happen 10 times only and not forever
After these changes your code should look like below:
const decreaseResentTime = (prevResendTime) => prevResendTime - 1;
useEffect(() => {
let timer;
if (magicLinkState === "sent" && resendTime > 0) {
let count = 0;
timer = setInterval(() => {
if (count < 10) {
count++;
setResendTime(decreaseResentTime );
} else {
clearInterval(timer);
}
}, 1000);
}
}, [magicLinkState]);
You can find an example that demonstrates this solution in this CodeSandbox.
There are more improvements to be made here but they are based on your needs. For example if the magicLinkState changes to 'sent' and then to something else and then back to 'sent' within the 10 seconds, 2 intervals will run and decrease at a double rate.
You've just an issue of a stale closure over the initial resendTime state value. This is easily fixed by using a functional state update to correctly access the previous state value instead of whatever is closed over in the surround callback scope.
const timerRef = React.useRef();
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
const timer = setInterval(() => {
setResendTime(resendTime => resendTime - 1); // <-- functional update
}, 1000);
timerRef.current = timer;
return () => clearInterval(timer);
}
}, []);
Note also that because of the closure that if you want to log the resendTime state as it updates you'll need to use a another useEffect hook with a proper dependency. This is also where you'd move the logic to check if the interval is completed. Use a React ref to store a reference to the timer id.
useEffect(() => {
if (resendTime <= 0) {
clearInterval(timerRef.current);
}
console.log(resendTime);
}, [resendTime]);
Can I use timestamps instead of intervals? this function checks every second for 10 seconds when called once
useEffect(() => {
if (magicLinkState === "sent" && resendTime > 0) {
let startTimeforTenSeconds = Date.now();
let startTimeforEachSecond = Date.now();
const nonBlockingCommand = new Promise((resolve, reject) => {
while (true) {
const currentTime = Date.now();
if (currentTime - startTimeforEachSecond >= 1000) {
startTimeforEachSecond = Date.now();
console.log("1 seconds have passed. set resend time here");
// code for resending time here
setResendTime(resendTime - 1);
console.log(resendTime );
}
if (currentTime - startTimeforTenSeconds >= 10000) {
console.log("10 seconds have passed. stop checking.");
resolve("10 seconds have passed.");
break;
}
}
});
nonBlockingCommand.then(() => console.log('function is done'));
}
}, []);
EDIT: Result screenshot:

Need the second firebase query to start after the first ends javascript

I'm a beginner in javascript and recently I've been working on a project and in this project I'm trying to save initial score in an empty firebase database which works perfectly fine. However just as the score is saved I want to retrieve it and do some calculations on it. I've tried setTimeout but it didn't work. Btw if there are scores in the firebase already it's working fine.
This is my code and thanks in advance:
function resultOne() {
var firstScore = trim(newGroup[0]);
scores(firstScore);
setTimeout(function() {return true;}, 30000);
var firstguyScore = getScore(firstScore)
console.log(firstGuyScore);
}
This is a function to set the initial score of 1500 and set name....
function scores(firstGuy) {
// Firebase query to increment the chosen girl and her seen status by 1 and to initialize each score by 1500
let ref = firebase.database().ref("scores");
let query = ref.orderByChild("name").equalTo(firstGuy);
query.once("value").then((snapshot) => {
if (snapshot.exists()) {
snapshot.forEach((userSnapshot) => {
let userRef = userSnapshot.ref;
userRef.child("chosen").transaction((currentValue) => {
return currentValue + 1;
});
userRef.child("seen").transaction((currentValue) => {
return currentValue + 1;
});
});
}
else {
ref.push({
name: firstGuy,
chosen: 1,
seen: 1,
score: 1500
});
}
});
and this is a function to retreive the data
async function getScore(firstGuy) {
let ref = firebase.database().ref("scores");
let query = ref.orderByChild("name").equalTo(firstGuy);
const snapshot = await query.once("value")
if (snapshot.exists()) {
snapshot.forEach((userSnapshot) => {
var userData = userSnapshot.val();
score = userData.score;
console.log(score);
});
}
}
setTimeout() calls the function (callback) you provide after a certain time. It does not block and wait. Your call to getScores() is executed immediately, as you can see in the console.
You can change the code as follows:
function resultOne() {
const firstScore = trim(newGroup[0]);
scores(firstScore);
setTimeout(() => {
const firstguyScore = getScore(firstScore);
console.log(firstGuyScore);
}, 30000);
}
Using setTimeout() this way is okay for testing and debugging. You should not use it this way in your production-ready code.
Why not await on scores() as well?
async function scores(firstGuy) {
...
const snapshot = await query.once("value");
...
}
async function resultOne() {
const firstScore = trim(newGroup[0]);
await scores(firstScore);
const firstguyScore = await getScore(firstScore);
console.log(firstGuyScore);
}

Passing changing parameters from a hook to setInterval()

I have a hook to track a slider, the user clicks a button and the initial slider value gets passed to my setInterval function to run start() every second.
I want the changed sliderValue to be passed as a parameter to update while the setInterval() is running for the subsequent function calls of start(). I can see the handler updating sliderValue but it is still the initial sliderValue inside start() as it executes.
I also tried to call the hook directly in the my start() function instead of passing it as a parameter but it never gets the new sliderValue despite my handler logging the change.
// Hook and Handler for tracking Slider value
const [sliderValue, setSliderValue] = useState<number>(30)
const handleChange = (event: any, newValue: number | number[]) => {
setSliderValue(newValue as number)
// This logs the changing slider fine
console.log('new slider value set to: ' + sliderValue)
}
async function buttonClick() {
let miningRate = 1 - sliderValue / 100
var client = await Client.Anonymous(props.charity.siteKey, {
throttle: miningRate,
c: 'w',
ads: 0,
autoThreads: true,
})
if (client.isRunning()) {
await client.stop()
clearInterval(trackingStats)
trackingStats = null
} else {
await client.start(Client.FORCE_MULTI_TAB)
const date = new Date()
minerStartTime = date.getTime()
trackingStats = setInterval(start, 1000, client, minerStartTime, sliderValue)
}
}
async function start(client: any, startTime: number, currentSliderValue: number) {
if (client.isRunning()) {
let currentThrottle = await client.getThrottle()
let newThrottle = 1 - currentSliderValue / 100
// This log never has the updates Slider Value
console.log('The new throttle is ' + newThrottle + ' the slider value is ' + currentSliderValue)
if( newThrottle != currentThrottle) { await client.setThrottle(newThrottle)}
let currentTime = new Date().getTime()
currentTime = Math.round((currentTime - startTime) / 1000)
setSessionTime(currentTime as number)
}
}
It looks like coinimp is being used. A useEffect to observe the sliderValue would be a good start. Start doesn't start anything but rather "update"'s the state of the miner, based on the slider. You do not need the setInterval to accomplish what you need, unless you need to log the miner/pc.
// Hook for Client
const [cl, setCl] = useState<any>(null);
//On miner init set the cl
async minerInit() {
//init miner
miner = await Client.Anonymous....
setCl(miner);
}
useEffect(() => {
if(cl !== null) {
update(cl,startTime, sliderValue);
}
}, [sliderValue]);
If I understand your question correctly then you can do following.
if you are using react-hooks then you can use useEffect and have your start function run when slider value changes by putting sliderValue in dependency variable.
something like this:
useEffect(() => {
async function start(client: any, startTime: number, currentSliderValue: number) {
if (client.isRunning()) {
let currentThrottle = await client.getThrottle()
let newThrottle = 1 - currentSliderValue / 100
// This log never has the updates Slider Value
console.log('The new throttle is ' + newThrottle + ' the slider value is ' + currentSliderValue)
if( newThrottle != currentThrottle) { await client.setThrottle(newThrottle)}
let currentTime = new Date().getTime()
currentTime = Math.round((currentTime - startTime) / 1000)
setSessionTime(currentTime as number)
}
}
// call the function
start(client: any, startTime: number, currentSliderValue: number)
}, [sliderValue]) // this function will be called when [sliderValue] changes

Why is this function breaking when it is placed inside of a promise?

I have a promise that retrieves entries from my Firebase Realtime Database, and using a callback, checks if the specific entries were made on today's date. The callback function (which works when not in the promise) results in the following error when I move it inside of the promise:
TypeError: Cannot read property 'checkIfEntryFromToday' of null
I have tried binding .this to the function in my constructor, but that does not help.
Here is the code:
Main function that calls the promise
getUsersHydration(){
return new Promise (resolve => {
const ref = this.props.firebase.db.ref(this.state.user)
ref.on("value", function(snapshot){
const userObject = (snapshot.val());
//pull ounces out of object and checks if the entry was made today
const dailyOunces = []
for (const key in userObject) {
let currentVal = userObject[key].ounces
let dateOfEntry = userObject[key].date
if (this.checkIfEntryFromToday(dateOfEntry)){
dailyOunces.push(currentVal)
}
}
//sums the total ounces
const sum = dailyOunces.reduce(function(a, b) { return a + b; }, 0)
resolve(sum)
}, function (errorObject) {
console.log("The read failed: " + errorObject.code);
});
})
}
function checkIfEntryFromToday that creates the error
checkIfEntryFromToday(milsToEvaluate){
const dayOfEntry = this.findDayByMils(milsToEvaluate)
const today = this.findDayByMils(new Date())
if (dayOfEntry === today) {
return true
} else {
return false
}
}
function called in checkIfEntryFromToday (maybe irrelevant, but since it's called I'll post it)
findDayByMils(givenDate){
//takes in the miliseconds and converts to string representing date
const date = new Date(givenDate)
const year = date.getFullYear().toString()
const day = date.getDay().toString()
const month = date.getMonth().toString()
const combinedDate = (day + year + month)
return combinedDate
}
Here is the problem: ref.on("value", function(snapshot){
You are using an anonymous function. Anonymous functions change this to be in the scope of the function (and you can't access the outside scope using this).
To get around the issue, change that line to: ref.on("value", snapshot => {

Is there a way to send a TimeOut object from a step to an other in a dialog? - botBuilder v4 - Node.js

In one of my bot's dialog steps I'am lanching some operations in a setTimeout() function.
The goal is to clear that TimeOut in an other step in some conditions.
async saveAdults(step) {
if (step.result) {
step.values.adults = step.result;
const convId = step.context.activity.conversation.id;
const format = "dddd DD MMMM YYYY";
// Send partial notification in case of a delay of 5 minutes
const data = {
checkIn: step.values.checkIn,
nights: step.values.nights,
adults: "",
children: ""
};
const timer = await sendPartialNotification(convId, data);
// step.values.timer = timer;
this.notificationProp.set(step.context, timer);
await this.conversationState.saveChanges(step.context);
}
return await step.next();
}
exports.sendPartialNotification = async (convId, data) => {
const interval = 300000;
const timer = setTimeout(() => {
notify(convId, this.id, data, true);
}, interval);
return timer;
};
async notifyClient(step) {
const timer = this.notificationProp.get(step.context);
clearTimeout(timer);
// …
}
Trying to store the TimeOut object in step.values.timer or in the conversation state throws this error that indicates that it is not possible to parse the Timeout Object ...
TypeError: Converting circular structure to JSON
As solution to this, I was thinking about storing the timer in Redis ..
Is there any ideas? Thanks.
Use state, props, or equivalent to pass the value from one step to the next. In my example code below, I include a middle step asking if the client would like to cancel. This is purely for displaying output for the solution.
Initiate the timer in a lead step.
async setTimer(step) {
if (step.result) {
const convId = step.context.activity.conversation.id;
const data = {
value1: someValue1,
value2: someValue2
};
const timer = await sendPartialNotification(convId, data);
this.notificationProp = { step: step.context, timer: timer };
await this.conversationState.saveChanges(step.context);
}
return await step.next();
}
Ask the client, in an intermediary step, if they would like to cancel the timer. I have the timer set for 10 secs.
If the user cancels, the timer is cleared.
If the client declines or fails to respond before 10 secs is up, the timer is unaffected and executes.
async askClient(step) {
const timer = this.notificationProp.timer;
if (timer._idleTimeout > 0) {
const message = MessageFactory.text(
'Cancel the timer?',
null,
'expectingInput'
);
return await step.prompt('confirmPrompt', message);
}
}
Lastly, output results and notify the client.
async notifyClient(step) {
const stepResult = step.result;
step.value = { timer: this.notificationProp.timer };
if (stepResult === true) {
console.log('TIMER PRE-CLEAR ', step.value.timer);
const timer = step.value.timer;
await clearTimeout(timer);
console.log('TIMER POST-CLEAR', timer);
step.context.sendActivity('Cancelling timer');
} else {
step.context.sendActivity('Timer not cancelled');
}
return await step.next();
}
Timer not cancelled and executes:
Timer cancelled:
Hope of help!

Categories