How to run to if statements simultaneously in javascript? - javascript

I am developing an employee login system that calculates the total time employee was logged in including the break times etc so I have two components 1. which keeps calculating the time until the employee is logged in 2. which calculates the total time of break. So in this situtation I do not want the login time to stop when the break is punched instead I want that break and login time should be running together
const [isLoggedIn, setLoggedIn] = useState(true)
const [isBreakPunched, setBreakPunch] = useState(true)
const [breakTime, setBreakTime] = useState(0);
const [loginTime, setLoginTime] = useState(0);
useEffect(() => {
if (isLoggedIn) {
const interval = setInterval(() => {
setLoginTime(onlineTime + 1);
}, 1000);
return () => clearInterval(interval);
}
if (isBreakPunched){
const interval = setInterval(() =>{
setBreakTime(auxTime +1);
},1000);
return ()=> clearInterval(interval);
}
});
When this runs, the loginTime is only running because the first condition is for login time indeed so to run the breakTime I have to stop the loginTime. This code is treating both as if..else.. loop
My query is that I want to run these both simultaneously when condition is true of anyone, they should be independent from each other

setInterval( () => {
if (isLoggedIn) {
setLoginTime(onlineTime + 1);
}
if (isBreakPunched) {
setBreakTime(auxTime +1);
}
}, 1000);
This interval checks if someone is logged in to set the login time, and sets the break time if they are on a break. BTW, it doesn't make sense that you want the login times and break times to be set depending on each other's condition while being 'independent' of each other.

Related

How to prevent a web timer to throttle on inactive tab(Chrome)?

I'm building simple web timer to calculate time working on tasks. Say, I have a 5 min timer and when time is up, I receive a notification.
I build it storing the time in state. But when the tab (Chrome) is inactive, because I'm actually working, the time throttle and gather some delay. (Chrome does that to conserve memory).
import React, { useEffect } from "react";
import { useFinishSession } from "../../hooks/useFinishSession";
const TimerIsActive = ({ setTimer, timerState, isActive }) => {
const { seconds, minutes, initialSeconds, initialMinutes } = timerState;
const { decreaseSeconds, decreaseMinutes, setSessionMinutes, setSessionSeconds } = setTimer;
const { finishSession, loading, error } = useFinishSession();
useEffect(() => {
let interval: NodeJS.Timer;
// reduce seconds and minutes by 1
if (isActive) {
interval = setInterval(() => {
if (seconds > 0) {
decreaseSeconds();
}
// when timer is finised, restart
if (seconds === 0 && minutes === 0) {
finishSession();
setSessionMinutes(initialMinutes);
setSessionSeconds(initialSeconds);
clearInterval(interval);
}
if (seconds === 0) {
decreaseMinutes();
}
}, 1000);
return () => clearInterval(interval);
}
}, [isActive, seconds, minutes]);
return null;
};
export default TimerIsActive;
How I go around that?
Here'are the solutions I'm contemplating:
Instead of storing the time in state, I will store the "start time" using a new Date() and calculate the difference every second to get an accurate time. (difference in seconds) --> my main concern with this approach is that the time only "correct" when the tab regain "focus". Hence, the notification won't fire "in time" when the timer is over.
Using service worker to calculate the time in the background.
What is the best options?
Similar question:
Countdown timer 'delays' when tab is inactive?

Interval not being cleared

I'm using a ref to set an interval that's checking a buffer state of a media element.
The interval is successfully set, however, it's not being cleared.
const keepCheckingBuffer = useCallback(
() => {
const targetRef = !hasVideo ? audioRef.current : vidRef.current;
console.log(targetRef.readyState, targetRef.HAVE_FUTURE_DATA);
console.log('keep checking buffer');
if (targetRef.readyState <= targetRef.HAVE_FUTURE_DATA) {
bufferCheckInterval.current = setInterval(() => {
console.log('im running and interval!!!');
if (targetRef.readyState > targetRef.HAVE_FUTURE_DATA) {
setIsBuffered(true);
setIsVideoReadyToPlay(true);
}
}, 1000);
console.log('not buffering, setInterval');
} else {
console.log('clearInterval');
clearInterval(bufferCheckInterval.current);
}
},
[audioRef, vidRef, hasVideo],
);
I'd prefer not to use a useEffect since my component rerenders a lot (it is expected) and I don't want to tie the triggering to a state inside the dependecy array.
I also tried to remove the useCallback hook.
Thanks all,

React state disappearing

I have a react app that uses the MS Graph API (so it's a bit difficult to post a minimal reproducible example). It has a state variable called chats that is designed to hold the result of fetching a list of chats from the graph API. I have to poll the API frequently to get new chats.
I query the chats endpoint, build an array of newChats and then setChats. I then set a timeout that refreshes the data every 10 seconds (it checks for premature invocation through the timestamp property stored in the state). If the component is unmounted, a flag is set, live (useRef), which stops the refresh process. Each chat object is then rendered by the Chat component (not shown).
Here's the code (I've edited by hand here to remove some irrelevant bits around styles and event propagation so it's possible that typo's have crept in -- it compiles and runs in reality).
const Chats = () => {
const [chats, setChats] = useState({ chats: [], timestamp: 0 });
const live = useRef(true);
const fetchChats = () => {
if (live.current && Date.now() - chats.timestamp < 9000) return;
fetchData(`${baseBeta}/me/chats`).then(res => {
if (res.value.length === chats.chats.length) return;
const chatIds = chats.chats.map(chat => chat.id);
const newChats = res.value.filter(chat => !chatIds.includes(chat.id));
if (newChats.length > 0) {
setChats(c => ({ chats: [...c.chats, ...newChats], timestamp: Date.now() }));
}
setTimeout(fetchChats, 10000);
});
};
useEffect(() => {
fetchChats();
return () => (live.current = false);
}, [chats]);
return (
<div>
{chats.chats.map(chat => (
<Chat chat={chat} />
))}
</div>
);
};
The Chat component must also make some async calls for data before it is rendered.
This code works, for a second or two. I see the Chat component rendered on the screen with the correct details (chat member names, avatars, etc.), but almost before it has completed rendering I see the list elements being removed, apparently one at a time, though that could just be the way its rendered -- it could be all at once. The list collapses on the screen, showing that the chat state has been cleared out. I don't know why this is happening.
I've stepped through the code in the debugger and I can see the newChats array being populated. I can see the setChats call happen. If I put a breakpoint on that line then it is only invoked once and that's the only line that sets that particular state.
So, what's going on? I'm pretty sure React isn't broken. I've used it before without much trouble. What's changed recently is the inclusion of the refresh code. I'm suspicious that the reset is taking away the state. My understanding is that the fetchChats method will be rendered every time the chats state changes and so should see the current value of the chats state. Just in case this wasn't happening, I passed the chats state from the useEffect like this:
useEffect(() => {
fetchChats(chats);
return () => (live.current = false);
}, [chats]);
With the necessary changes in fetchChats to make this work as expected. I get the same result, the chats state is lost after a few seconds.
Edit
Still Broken:
After #Aleks answer my useEffect now looks like this:
useEffect(() => {
let cancel = null;
let live = true;
const fetchChats = () => {
if (Date.now() - chats.timestamp < 9000) return;
fetchData(`${baseBeta}/me/chats`).then(res => {
if (res.value.length === chats.chats.length) return;
const chatIds = chats.chats.map(chat => chat.id);
const newChats = res.value.filter(chat => chat.chatType === "oneOnOne" && !chatIds.includes(chat.id));
if (newChats.length > 0 && live) {
setChats(c => ({ chats: [...c.chats, ...newChats], timestamp: Date.now() }));
}
cancel = setTimeout(fetchChats, 10000);
});
};
fetchChats();
return () => {
live = false;
cancel?.();
};
}, []);
The result of this is that the chats are loaded, cleared, and loaded again, repeatedly. This is better, at least they're reloading now, whereas previously they would disappear forever. They are reloaded every 10 seconds, and cleared out almost immediately still.
Eventually, probably due to random timings in the async calls, the entries in the list are duplicated and the 2nd copy starts being removed immediately instead of the first copy.
There are multiple problems. First this
setTimeout(fetchChats, 10000); will trigger
useEffect(() => {
fetchChats(chats);
return () => (live.current = false);
}, [chats])
You will get 2 fetches one after another.
But the bug you're seeing is because of this
return () => (live.current = false);
On second useEffect trigger, clean up function above with run and live.current will be forever false from now on.
And as Nikki9696 said you you need to clear Timeout in clean up function
The easiest fix to this is, probably
useEffect(() => {
let cancel = null;
let live = true;
const fetchChats = () => {
// not needed
//if ( Date.now() - chats.timestamp < 9000) return;
fetchData(`${baseBeta}/me/chats`).then(res => {
//this line is not needed
//if (res.value.length === chats.chats.length) return;
// remove all the filtering, it can be done elsewhere where
// you can access fresh chat state
//const chatIds = chats.chats.map(chat => chat.id);
//const newChats = res.value.filter(chat =>
//!chatIds.includes(chat.id));
if (res.value?.length > 0&&live) {
setChats(c => ({ chats: [...c.chats, ...res.value], timestamp: Date.now() }));
cancel = setTimeout(fetchChats, 10000);
}
});
};
fetchChats()
return () => { live=false; if(cancel)window.clearTimeout(cancel) };
}, []);
Edit: typo cancel?.() to window.clearTimeout(cancel);
Ok, I have an idea what's happening and how to fix it. I am still not sure why it is behaving like this, so please comment if you understand it better than me.
Basically, for some reason I don't understand, the function fetchChats only ever sees the initial state of chats. I am making the mistake of filtering my newly fetched list against this state, in which the array is empty.
If I change my useEffect code to do this instead:
setChats(c => {
return {
chats: [
...c.chats,
...res.value.filter(cc => {
const a = c.chats.map(chat => chat.id);
return !a.includes(cc.id);
})
],
timestamp: Date.now()
};
});
Then my filter is passed the current value of the state for chats rather than the initial state.
I thought that because the function containing this code is in the function that declares the chat state, whenever that state changed the whole function would be rendered with the new value of chats making it available to its nested functions. This isn't the case here and I don't understand why.
The solution, to only trust the values of the state that is handed to me during the setState (setChats) call, works fine and I'll go with it, but I'd love to know what is wrong with reading the state directly.

Using React To Create a Loop That Updates A Number Value

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)

How to continuously check if the Date has passed in a chrome extension?

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.

Categories

Resources