I'm trying to get the interval of time when the user started typing until he submitted his text. When he clicks on the text area the timer starts and gets calculated in seconds (hours * 3600 + minutes * 60 + seconds). Same for the timer to end when he clicks submit.
Now my problem is that when I try to subtract the two values saved in this.state, even if I store them in a var first or if I do a this.setState(), I still get 'undefined'.
overallTime(e){
this.setState({Time: this.state.StartTime - this.state.EndTime})
console.log(this.state.Time)
}
onSubmit = (e) => {
this.overallTime();
var textinput = this.state.inputValue;
var inputLength = textinput.length;
var inputTime = this.state.Time;
if(textinput === this.state.Content)
{
console.log(inputLength)
var lps = inputLength / inputTime;
this.setState({LettersPerSecond: lps})
console.log(lps)
this.setState({message: 'Your score is ' + this.state.LettersPerSecond + ' letters per second! Good job! Refresh the page for another try!'})
}
else
{
this.setState({message: 'Your input is incorrect, refresh the page and start again! Remember, you always learn from your failures, so never give up!'})
}
}
I'd suggest restructuring like this:
overallTime() {
return this.state.EndTime - this.state.StartTime;
}
onSubmit = (e) => {
const totalTime = this.overallTime();
const textinput = this.state.inputValue;
const inputLength = textinput.length;
if (textinput === this.state.Content) {
const lps = inputLength / totalTime;
this.setState({
LettersPerSecond: lps,
message: 'Your score is ' + lps + ' letters per second! Good job! Refresh the page for another try!'
});
} else {
this.setState({
message: 'Your input is incorrect, refresh the page and start again! Remember, you always learn from your failures, so never give up!'
})
}
}
Why?
setState({Time: someValue});
// this.state.Time doesn't have the new value yet at this point
See – State updates may be asynchronous.
There's also a minor logical error – you need to do endTime - startTime, not startTime - endTime.
Your overallTime function is not exactly right – I think you meant something like this:
overallTime() {
this.setState((state) => ({
Time: state.EndTime - state.StartTime
});
}
I strongly recommend reading through the State and Lifecycle section of React docs – it's very well written.
You can use call back function of setState function. you will always get updated state in call back function.
for example:
overallTime(e){
this.setState({Time: this.state.StartTime - this.state.EndTime},
() => console.log(this.state.Time))
}
Related
Trying to create an auto-clicker/idle game. So far the entire application works except for this loop. After the loop begins, if I update the counter, different values update in intervals. So my counter will display those different values, going back and forth between them depending on how many times I've tried to mess with the counter while its looping.
I've tried using while loops, if statements, and for loops. And for each of those loops I've tried both setInterval() and setTimeout(). They either lead to the problem above, or the browser crashing.
Here's a video of the issue:
Youtube Link
Here's the relevant code I've got currently:
const Counter = () => {
const [counter, setCounter] = useState(1);
const [minions, setMinions] = useState(0);
let minionCost = minions * 10 + 6;
let autoMinions = () => {
if (minions > 0) {
setTimeout(() => {
setCounter(minions + counter);
}, 1000);
} else {
return null;
}
};
const onClickMinion = () => {
if (counter < minionCost) {
console.log(`you don't have ${minionCost} to spend`);
} else {
setCounter(counter - minionCost);
setMinions(minions + 1);
}
};
autoMinions();
};
If you're computing state based off of a previous state, you should use functional updates.
Try passing setCounter a function that receives the previous state instead of using counter directly (do this with any of your useState hooks that depend on previous state):
setCounter(prevCounter => prevCounter + minions)
I am building a Chrome Extension and I am letting the user choose a Time when they are usually turning the PC off.
If this time has passed, I want a value to be reset back to 0 and a new Date be created.
What I did
I created a function that takes a parament of a Dare ISO String, which will then be converted into a Date Object. Inside that function I am comparing between now and the end time, and if the end time is smaller or equal to now, it means the time has passed and the value should be reset. But it's not doing anything.
I call the function inside my storage.sync.get method and inside my storage.onChanged method, so I always have the correct time to work with. But that does not seem to do it.
Here's the code:
Background.js
chrome.storage.onChanged.addListener((changes, namespace) => {
if ("reset" in changes) {
const reset = changes.reset.newValue;
console.log(reset);
checkResetTimer(reset);
}
});
chrome.storage.sync.get(["reset", "amount"], (obj) => {
const reset = obj.reset;
console.log(reset);
checkResetTimer(reset);
});
function checkResetTimer(isoTime) {
const resetDate = new Date(isoTime);
const now = new Date();
if (resetDate <= now) {
chrome.storage.sync.set({ drank: 0 }, () => {
console.log("drank has been set.");
});
}
}
The time value I get from the popup, it's an input.
I am at a loss right now. I don't know how to properly have a reset timer.
You can view my whole code in this Repository: https://github.com/Braweria/TakeAGulp
I feel the problem is, that it checks only once, but it needs to check the time consistently.
A crude approach to the problem can be the following:
Background.js
// rest of your code
const resetInterval = setInterval(() => {
chrome.storage.sync.get(["reset", "amount"], (obj) => {
const reset = obj.reset;
const resetTime = new Date(reset);
const now = new Date();
if(resetTime < now) {
// past your reset time; reset the value here
// maybe clear the interval too to stop the checking
clearInterval(resetInterval);
}
});
}, 1000 * 60); // check every minute
Essentially you have to check the value of the reset timer at a given interval to make sure whether that timer has expired.
I'm trying to delete multiple nodes on my database that are older than 12hrs. I"m using a pub/sub function to trigger this event. I don't know if my code is actually looping through all nodes as I'm not using the onWrite, onCreate database triggers on specific. Here is the image sample of the database
this is the pub/sub code
exports.deletejob = functions.pubsub.topic('Oldtask').onPublish(() => {
deleteOldItem();
})
and the deleteOldItem function
function deleteOldItem(){
const CUT_OFF_TIME = 12 * 60 * 1000; // 12 Hours in milliseconds.
//var ref = admin.database().ref(`/articles/${id}`);
const ref = admin.database().ref(`/articles`);
const updates = {};
ref.orderByChild('id').limitToLast(100).on('value', function (response) {
var index = 0;
response.forEach(function (child) {
var element = child.val();
const datetime = element.timestamp;
const now = Date.now();
const cutoff = now - datetime;
if (CUT_OFF_TIME < cutoff){
updates[element.key] = null;
}
});
//This is supposed to be the returened promise
return ref.child(response.key).update(updates);
});
If there's something I'm doing wrong, I'll like to know. The pub/sub is triggered with a JobScheduler already setup on google cloud scheduler
You had several problems in your code that were giving you trouble.
The handling of promises wasn't correct. In particular, your top level function never actually returned a promise, it just called deleteOldItems().
You should use the promise form of once() instead of calling on() with a callback since you don't want to install a listener in this case, you just need the result a single time, and you want to handle it as part of a promise chain.
To delete nodes, you should call remove() on a reference to that node. It also generates a promise for you to use here.
You didn't calculate 12 hours in milliseconds properly, you calculated 12 minutes in milliseconds :)
Here's what I came up with. It uses an http function instead of a pubsub function as well as adding a log statement for my testing, but the modification you need should be trivial/obvious (just change the prototype and remove the response after deleteOldItems, but do make sure you keep returning the result of deleteOldItems()):
const functions = require('firebase-functions');
const admin = require('firebase-admin');
function deleteOldItems() {
const CUT_OFF_TIME = 12 * 60 * 60 * 1000; // 12 Hours in milliseconds.
const ref = admin.database().ref('/articles');
return ref.orderByChild('id').limitToLast(100).once('value')
.then((response) => {
const updatePromises = [];
const now = Date.now();
response.forEach((child) => {
const datetime = child.val().timestamp;
const cutoff = now - datetime;
console.log(`processing ${datetime} my cutoff is ${CUT_OFF_TIME} and ${cutoff}`);
if (CUT_OFF_TIME < cutoff){
updatePromises.push(child.ref.remove())
}
});
return Promise.all(updatePromises);
});
}
exports.doIt = functions.https.onRequest((request, response) => {
return deleteOldItems().then(() => { return response.send('ok') });
}
While I have not tested it, I'm pretty sure this will work to include inside your original function call for cloud scheduler:
exports.deletejob = functions.pubsub.topic('Oldtask').onPublish(() => {
return deleteOldItems();
})
Of course, this is still more complicated than you need, since ordering by id doesn't really gain you anything here. Instead, why not just use the query to return the earliest items before the cut off time (e.g. exactly the ones you want to remove)? I've also switched to limitToFirst to ensure the earliest entries get thrown out, which seems more natural and ensures fairness:
function deleteOldItems() {
const cutOffTime = Date.now() - (12 * 60 * 60 * 1000); // 12 Hours earlier in milliseconds.
const ref = admin.database().ref('/articles');
return ref.orderByChild('timestamp').endAt(cutOffTime).limitToFirst(100).once('value')
.then((response) => {
const updatePromises = [];
response.forEach((child) => {
updatePromises.push(child.ref.remove())
});
return Promise.all(updatePromises);
});
}
If you do this on more than a few items, of course, you probably want to add an index on the timestamp field so the range query is more efficient.
I'm trying to add a 1 second cooldown to my send-message system (as in, you can send 1 message per second max). So my initial thought was simply to create a timeout, and before attempting in sending to check if it exists still. That turned out to take more line of code than I anticipated initially.
Is there something I'm missing here? Isn't there something as simple as:
//inside some message sending function
if(!mySuperCooldown)
{
//send message
mySuperCooldown = cooldown(1000);
}
Everything else I construct successfully ends up taking loads of lines, and it appears to me as something someone thought of before. Thank you, and excuse my illiteracy.
Have a flag that allows messages, and set it to false when a message is sent. Then set a timeout for 1000 milliseconds that resets the flag to true.
var allowMessage = true;
function sendMessage(msg) {
if (allowMessage) {
//do something
allowMessage = false;
setTimeout(() => allowMessage = true, 1000);
}
}
Make a higher order function that turns a normal function into one that is rate limited:
function rate_limit(delay, func) {
var last_call = null;
return function() {
if (last_call && (Date.now() - last_call <= delay)) {
return;
}
last_call = Date.now();
return func();
};
}
You can then rate limit any function:
var my_function = rate_limit(1000, function() {
console.log('foo');
});
Running my_function() will only call your original function once per second.
In our application, we have some questions to answer that will update a progress bar. Currently, I have a function that waits for HTML Attribute changes which works for most things, but it's a little finicky for the progress bar since the animation occurs over 1-2 seconds as the bar moves from 0 - 10% etc. So the failure I'm currently facing is things like: Expected 11 to be within range 12, 14.
Code:
Util.prototype.waitForAttributeChange = function (el, attr, time) {
var timeout = time || 0,
currentAttr;
el.getAttribute(attr).then(function (val) {
currentAttr = val;
return currentAttr;
}).then(function () {
return browser.wait(function () {
return el.getAttribute(attr).then(function (val) {
return val !== currentAttr;
});
}, timeout);
});
};
Usage:
Util.waitForAttributeChange(Page.progressBar(), 'style', 10000).then(function () {
expect(Page.getProgressBarValue()).toBeWithinRange(12, 14);
};
Problem: The value grabbed is not the end result of the progress bar, it's still moving when it's grabbing it (because my function waits for Attribute changes, and the attribute did change at this point)
Question: Is there another way I can wait for an animation, specifically waiting for it to be completed? And/or is this possible without using browser.sleep()?
You might be able to solve this problem by using Expected Conditions.
I use the below methods whenever I need to wait for an element to be visible then wait for it to go away before executing the next step. This is helpful for temporary confirmation modals that may block interaction with other elements.
let waitTimeInSeconds = 15;
let EC = protractor.ExpectedConditions;
secondsToMillis(seconds) {
return seconds * 1000;
}
waitToBeVisible(element: ElementFinder) {
browser.wait(EC.visibilityOf(element), this.secondsToMillis(waitTimeInSeconds), 'The element \'' + element.locator() + '\' did not appear within ' + waitTimeInSeconds + ' seconds.');
}
waitToNotBeVisible(element: ElementFinder) {
browser.wait(EC.not(EC.visibilityOf(element)), this.secondsToMillis(waitTimeInSeconds), 'The element \'' + element.locator() + '\' still appeared within ' + waitTimeInSeconds + ' seconds.');
}