Passing changing parameters from a hook to setInterval() - javascript

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

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:

console log messages not being shown in browser console

I have this function in my Backbone view for creating a new object through an API in the backend:
// *** called when remote hardware signal triggered
createConference: function () {
var self = this;
console.log("ScheduleLocationArea.js - createConference() ")
const startTime = performance.now();
this.sysLocation.create().then((response) => {
self.model.collection.add(response);
});
const duration = performance.now() - startTime;
console.log(`PERFORMANACE CHECK: ScheduleLocationArea.js - the function createConference() took ${duration}ms`);
},
It calling this function:
// called from within createConference
async create() {
console.log("Location.js - create() ")
const startTime = performance.now();
return await this.sync('create', this, {
url: this.create.url(this.id)
}, { silent: true });
const duration = performance.now() - startTime;
console.log(`PERFORMANACE CHECK: Location.js - the function create() took ${duration}ms`);
},
As you can see, I'm trying to check performance issues.
But for some reason that I cant figure out, it's not finishing the create() function. I never see the PERFORMANACE CHECK for that function.
Here is my console output:
ScheduleLocationArea.js - createConference()
Location.js:22 Location.js - create()
ScheduleLocationArea.js:269 PERFORMANACE CHECK: ScheduleLocationArea.js - the function createConference() took 1.7000000476837158ms
The browser writes out all the above console messages really fast.
And even though it says it only took 1.7ms...it actually takes about 3 seconds.
So I can't figure out whats taking so long and why it's not writing out the performance numbers for the create() function.
Is there something I'm doing wrong?
Thanks!
Change your code from:
// called from within createConference
async create() {
console.log("Location.js - create() ")
const startTime = performance.now();
return await this.sync('create', this, {
url: this.create.url(this.id)
}, { silent: true });
const duration = performance.now() - startTime;
console.log(`PERFORMANACE CHECK: Location.js - the function create() took ${duration}ms`);
},
to
// called from within createConference
async create() {
console.log("Location.js - create() ")
const startTime = performance.now();
const newUrl = await this.sync('create', this, {
url: this.create.url(this.id)
}, { silent: true });
const duration = performance.now() - startTime;
console.log(`PERFORMANACE CHECK: Location.js - the function create() took ${duration}ms`);
return newUrl;
},
This will allow your function to show the performance logs before returning the created value.
You are returning from your function before calling console.log in your first snippet. Any code following a return statement isn't run:
// called from within createConference
async create() {
console.log("Location.js - create() ")
const startTime = performance.now();
return await this.sync('create', this, {
url: this.create.url(this.id)
}, { silent: true });
// this following code never runs as screate(0 has returned already
const duration = performance.now() - startTime;
console.log(`PERFORMANACE CHECK: Location.js - the function create() took ${duration}ms`);
},

Is it possible to keep setInterval on multiple devices 'synced' by compensating for drift, or do I need server synchronisation?

I am building a React Native app where my entire back end is provided for by services like Firebase etc.
The app requires clocks on multiple devices to start and end at the same time which can run for up to an hour.
Given a shared starting point in time between devices I have observed drift in the accuracy of setInterval in this 20 seconds of data:
I am attempting to compensate for this deviation in clock timing by measuring it and then compensating for it - here is a code sandbox with my solution.
useTimer hook:
import { useState, useEffect, useRef } from "react";
import moment from "moment";
export const convertMsToMinsAndSecs = (countDown) => {
const seconds = moment
.duration(countDown)
.seconds()
.toString()
.padStart(2, "0");
const minutes = moment
.duration(countDown)
.minutes()
.toString()
.padStart(2, "0");
const minsAndSecs = `${minutes.toString()}:${seconds.toString()}`;
return countDown > 0 ? minsAndSecs : "00:00";
};
const roundTimeStamp = (timeStamp) =>
timeStamp === 0 ? 0 : timeStamp + (1000 - (timeStamp % 1000));
export const useTimer = (
started,
startTime,
length,
resetClock,
clockIntialState
) => {
const initialTimerState = {
start: 0,
end: 0,
timeNow: 0,
remaining: length,
clock: convertMsToMinsAndSecs(length),
internalClockDeviation: 0
};
const [timeData, setTimeData] = useState(initialTimerState);
const intervalId = useRef(null);
const deviation = useRef(null);
useEffect(() => {
setTimeData((prevState) => ({
...prevState,
start: roundTimeStamp(startTime),
end: roundTimeStamp(startTime) + length
}));
if (started) {
intervalId.current = setInterval(() => {
const intervalTime = moment().valueOf();
setTimeData((prevState) => {
return {
...prevState,
timeNow: intervalTime,
remaining: prevState.remaining - 1000,
clock: convertMsToMinsAndSecs(prevState.remaining - 1000),
internalClockDeviation:
prevState.timeNow === 0
? 0
: intervalTime - prevState.timeNow - 1000
};
});
}, 1000 - deviation.current);
}
}, [started]);
useEffect(() => {
deviation.current = timeData.internalClockDeviation;
}, [timeData.internalClockDeviation]);
if (timeData.remaining <= 0 && started) {
resetClock(clockIntialState);
clearTimeout(intervalId.current);
setTimeData(initialTimerState);
}
const compensatedLength = 1000 - deviation.current;
return {
timeData,
intervalId,
compensatedLength,
setTimeData,
initialTimerState
};
};
As I am not running my own server application I would prefer to handle this on the client side if possible. It also means that I do not need to rely on network connections or the availability of a timing server.
Will my approach work across multiple devices, and if so can it be improved, or do I need to build a server side application to effectively handle this? TIA.
When you determine time diff you can not rely on intervals being accurate. Gets worse when tab is in background/not in focus.
Typically you rely on timestamps to get the offset in time, you do not subtract a fix number.
function countDown(totalTime, onComplete, onUpdate, delay = 1000) {
let timer;
const startTime = new Date().getTime();
function next() {
const runningTime = new Date().getTime() - startTime;
let remaining = Math.max(totalTime - runningTime, 0);
onUpdate && onUpdate(remaining);
!remaining && onComplete && onComplete();
var ms = Math.min(delay, remaining);
timer = remaining && window.setTimeout(next, ms);
}
next()
return function () {
timer && window.clearTimeout(timer);
}
}
countDown(5000, function(){ console.log('done1'); }, function(x){ console.log('update1 ', x); });
const out = document.getElementById("out");
const cd = countDown(
60000,
function(){ out.textContent = 'done'; },
function(x){ out.textContent = (x/1000).toFixed(3); },
20
);
document.getElementById("btn").addEventListener('click', cd);
<div id="out"></div>
<button id="btn">stop</button>
This will fail if user changes clock, not much you can do on that. You could ping the server for time, but that also has latency with how long the call takes.
The only way you can synchronize all the clocks together is having all of them observe one single source of truth.
If you create the timestamp on one client. You need to make sure all the other clients are in sync with that data.
What you can do is have a server + client architecture where the server is the single point of truth. But if you are trying to completely synchronize without a single point of truth, you are doomed to fail because of the problems that you can not control like latency of communication between all the client applications in your case clock.

ReactJS useState not updating momentJS prop

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]);

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