My counter stop when i switch tab (reactjs) - javascript

I am new to javascript, and I made a counter. It works, however when the tab is inactive it stops and then resumes when I return to the page.
Here is my code:
import React, { useEffect, useState } from 'react'
function TimeTracker(props){
const [time, setTime] = useState(0);
const [timerOn, setTimerOn] = useState(false);
useEffect(()=>{
let interval = null;
if(timerOn){
interval = setInterval(()=>{
setTime(prevTime => prevTime +10)
}, 10)
}else{
clearInterval(interval)
}
return ()=> clearInterval(interval)
},[timerOn])
return(
<div>
<p>{("0" + Math.floor((time / 60000) % 60)).slice(-2)} mn</p>
<p>{("0" + Math.floor((time / 1000) % 60)).slice(-2)} s</p>
<button onClick={()=>setTimerOn(true)}>Start</button>
<button onClick={()=>setTimerOn(false)}>Stop</button>
</div>
)
}
Thank you in advance for any help you can give me.

On most browsers inactive tabs have low priority execution and this can affect JavaScript timers.
Thus, when you not focus on your tab, interval will not work. Its not React problem.
Check this question, I wish it help you.

Your counter was not efficient
If you add a console.log before setTime and start the counter you will notice the extra renders
The code below will solve your issue:
import React, { useEffect, useState } from 'react'
function TimeTracker(props){
const [time, setTime] = useState(0);
const [timerOn, setTimerOn] = useState(false);
useEffect(()=>{
let interval = null;
if(timerOn){
interval = setInterval(()=>{
setTime(prevTime => prevTime +1)
}, 1000)
}else{
clearInterval(interval)
}
return ()=> clearInterval(interval)
},[timerOn])
return(
<div>
<p>0{Math.floor(time / 60)} mn</p>
<p>{Math.floor(time % 60)} s</p>
<button onClick={()=>setTimerOn(true)}>Start</button>
<button onClick={()=>setTimerOn(false)}>Stop</button>
</div>
)
}

Related

Refreshing and updating useState/useRef in React

Trying to update and read state in React to switch between two different timers. I'm still new to programming and can't figure out why my component that displays the state "Session" or "Break" updates, but my conditional switchTimers() fails to switch timers based on that state.
const [timers, setTimers] = useState({
sessionTime: 25,
breakTime: 5,
});
const [timerDisplay, setTimerDisplay] = useState("Session");
const [timerActive, setTimerActive] = useState(false)
const [displayCount, setDisplayCount] = useState(1500);
const round = useRef();
function startStop(action, secondsLeft) {
const interval = 1000;
let expected = Date.now() + interval;
if (action === "start") {
setTimerActive(true)
round.current = setTimeout(step, interval);
function step() {
if (secondsLeft > 0) {
const drift = Date.now() - expected;
setDisplayCount((prevValue) => prevValue - 1);
secondsLeft --
expected += interval;
round.current = setTimeout(step, Math.max(0, interval - drift));
} else {
clearTimeout(round.current)
switchTimers()
}
}
} else if (action === "stop") {
clearTimeout(round.current);
setTimerActive(false);
}
}
function switchTimers() {
beep.current.play()
if (timerDisplay === "Session") {
setTimerDisplay("Break");
setDisplayCount(timers.breakTime * 60);
startStop("start", timers.breakTime * 60)
} else if (timerDisplay === "Break") {
setTimerDisplay("Session");
setDisplayCount(timers.sessionTime * 60);
startStop("start", timers.sessionTime * 60)
}
}
When the "Session" timer ends, it shows "Break" in my label that prints timerDisplay, but once "Break" timer ends, it reruns "Break" instead of switching to "Session". Any insight into whats going wrong?
Try writing you entire switchTimers function as a callback to the useEffect hook and add timerDisplay as a dependency. Also, please show the JSX you return from the component i.e. the entire code so that I can help you better.

Different speed results between Chrome(Edge) and Mozilla on this stopwatch

Dear all professionals from everywhere.
I have a question from a beginner. I did not find any direct answers only some references...
I wrote a simple version of stopwatch (React) and when I try to test it on different browsers (Chrome, Mozilla and Edge) I have different speed results between Chrome(Edge) and Mozilla on this stopwatch. The difference is about 4 times.
I would be very grateful if you also send me a link with some theory (maybe).
Anyway thank you
P.S If you see something that seems strange in this code please tell me. That would be very useful for me.
import React, { useState } from "react";
import { useEffect } from "react";
import "./Stopwatch.css";
import Button from "./Button";
import Round from "./Round";
function Stopwach() {
const [timer, setTimer] = useState(0);
const [start, setStart] = useState(false);
const [round, setRound] = useState([]);
const [globeTimer, setGlobeTimer] = useState(0);
useEffect(() => {
let secondsCircle = document.querySelector(".svg01");
let milCircle = document.querySelector(".svg02");
let degreeS = 0;
let degreeM = 0;
degreeS = timer / 166.6666666666667;
secondsCircle.style.transform = `rotate(${degreeS}deg)`;
degreeM = timer / 3.6;
milCircle.style.transform = `rotate(${degreeM}deg)`;
}, [timer]);
function Circle() {
if (round.length < 10) {
if (round.length === 0) {
round[round.length] = timer;
setRound(round);
} else {
round[round.length] = timer - globeTimer;
setRound(round);
}
} else {
let firstElement = Math.min.apply(Math, round);
round.length = 0;
round[0] = firstElement;
round[round.length] = timer - globeTimer;
setRound(round);
}
setGlobeTimer(timer);
}
useEffect(() => {
let interval;
if (start) {
interval = setInterval(() => {
setTimer((timer) => timer + 4);
}, 1);
} else {
clearInterval(interval);
}
return () => {
clearInterval(interval);
};
}, [start]);
function go() {
setStart(!start);
}
function clear() {
setTimer(0);
setRound([]);
setGlobeTimer([])
}
return (
<div className="main_monitor">
<div className="svg">
<div className="svg01"></div>
<div className="svg02"></div>
</div>
<div className="main_monitor__timer">
<div className="mill">
{Math.trunc(timer / 1000 / 60) < 10 ? `0${Math.trunc(timer / 1000 / 60)}` : Math.trunc(timer / 1000 / 60)}
</div>
<div className="point">:</div>
<div className="mill">
{Math.trunc(timer / 1000) % 60 < 10 ? `0${Math.trunc(timer / 1000) % 60}` : Math.trunc(timer / 1000) % 60}
</div>
<div className="point">.</div>
<div className="mill">{timer % 1000 < 10 ? `00${timer % 1000}` : timer % 1000 < 100 ? `0${timer % 1000}` : timer % 1000} </div>
</div>
<div className="main_monitor__btns">
<Button go={go} btn={"play"} />
<Button go={go} btn={"stop"} />
<Button go={clear} btn={"recycle"} />
<Button go={Circle} btn={"history"} />
</div>
<Round rounds={round} />
</div>
);
}
export default Stopwach;
So, after 5 days and hole bunch of coffee...
According to Wiki - Firefox, Google and Edge is browsers with different engines.
I think, at least, it`s the root of this mistake.
I solved this with Date() constructor. Every time when we call setInterval - we will ask new "a single moment in time in a platform-independent format" (unfortunatly this part of definition was not translated (en - ru) on MDN). And based on this "single moment" we can calculate how much time passed from our time point...

Multiple States in React doesn't update which are dependent on other state

I am trying to update the State B when the value of State A fulfills the criteria but the state B isnt changing.
If the below code is run the value of B should increase by +1 whenever A reaches 100 , But when displayed the value of B remains fixed to 0
Here the pseudo code of what i am trying to do
import React , {useState} from "react";
export default function Test() {
const [A , setA] = useState(0);
const [B, setB] = useState(0);
const handleStart = () => {
setInterval( () => {
setA(prev => prev + 1)
if( A === 100){
setB(prev => prev +1)
A = 0
}
},10)
}
return (
<div>
<h1>{A}</h1>
<h1>{B}</h1>
<button onClick = {() => {handleStart()}}> START </button>
</div>
);
}
setA() method is an async method which is updating values asynchronously so your if condition before the setA may trigger first as React creates a batch of these async task. You need to move the logic into SetA() to make it working.
Code -
const handleStart = () => {
setInterval(() => {
setA((prev) => {
if (prev === 100) {
setB((prevB) => prevB + 1);
return 0;
}
return prev + 1;
});
}, 10);
};
Working Example - codesandbox Link

React JS: State become 'undefined' when working with useEffect

I am working on typewriter effect in React JS. I have previously implemented typewriter effect in Vanilla JS.
The React JS code that I am writing is
import React, { useEffect, useRef, useState } from 'react'
const Typewriter = () => {
const el = useRef();
const words = ["Life", "Could Be", "A Dream"];
const [index, setIndex] = useState(0);
const [subIndex, setSubIndex] = useState(0);
const [isEnd, setIsEnd] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [duration, setDuration] = useState(200);
const [currentWord, setCurrentWord] = useState("");
const spedUp = 50;
const normalSpeed = 200;
useEffect(() => {
const interval = setInterval(() => {
setIndex(index % words.length);
setIsEnd(false);
if (!isDeleting && subIndex <= words[index].length) {
setCurrentWord((prev) => prev.concat(words[index][subIndex]));
setSubIndex((prev) => prev += 1);
el.current.innerHTML = currentWord;
}
if (isDeleting && subIndex > 0) {
setCurrentWord((prev) => prev.substring(0, prev.length - 1));
setSubIndex((prev) => prev -= 1);
el.current.innerHTML = currentWord;
}
if (subIndex === words[index].length) {
setIsEnd(true);
setIsDeleting(true);
}
if (subIndex === 0 && isDeleting) {
setCurrentWord("");
setIsDeleting(false);
setIndex((prev) => prev += 1);
}
setDuration(isEnd ? 1500 : isDeleting ? spedUp : normalSpeed);
}, duration);
return () => clearInterval(interval);
}, [subIndex, index, currentWord, isEnd, isDeleting, duration, words])
return (
<>
<h1 ref={el}>Placeholder text</h1>
</>
)
}
export default Typewriter
When I run the React App, the phrases/words are appended with 'undefined' at the end. The application displays the 3 phrases once and then the app crashes because the code tries to get the length of an undefined array element.
Here are the visuals
React Application GIF
Can someone explain to me what is happening and are the dependencies used for useEffect hook correct or not?
Main issue
You are using set(X) and making an assumption that the value will be immediately set and that you can base later conditions on it. useState does not actually set the state immediately -- it happens on the next loop, so any conditions you have (like testing the index after setIndex) are going to go awry because they're using stale values. See useState set method not reflecting change immediately
I avoided this by using interim values (newIndex, newSubIndex) -- I'm not wild about this approach -- I'd probably refactor with a different strategy, but it was the quickest way to get this up and running.
import React, { useEffect, useRef, useState } from 'react'
const Typewriter = () => {
const words = ["Life", "Could Be", "A Dream"];
const [index, setIndex] = useState(0);
const [subIndex, setSubIndex] = useState(0);
const [isEnd, setIsEnd] = useState(false);
const [isDeleting, setIsDeleting] = useState(false);
const [duration, setDuration] = useState(200);
const [currentWord, setCurrentWord] = useState("");
const spedUp = 50;
const normalSpeed = 200;
useEffect(() => {
const interval = setInterval(() => {
let newSubIndex = subIndex;
let newIndex = index % words.length;
setIsEnd(false);
if (!isDeleting && subIndex < words[newIndex].length) {
setCurrentWord((prev) => prev.concat(words[newIndex][subIndex]));
newSubIndex += 1;
}
if (isDeleting && subIndex > 0) {
setCurrentWord((prev) => prev.substring(0, prev.length - 1));
newSubIndex -= 1;
}
if (newSubIndex === words[newIndex].length) {
setIsEnd(true);
setIsDeleting(true);
}
if (newSubIndex === 0 && isDeleting) {
setCurrentWord("");
setIsDeleting(false);
newIndex += 1;
}
setSubIndex(newSubIndex);
setIndex(newIndex);
setDuration(isEnd ? 1500 : isDeleting ? spedUp : normalSpeed);
}, duration);
return () => clearInterval(interval);
}, [subIndex, index, currentWord, isEnd, isDeleting, duration, words])
return (
<>
<h1>{currentWord}</h1>
</>
)
}
export default Typewriter
Minor issue
In React, rather than using a ref and editing innerHTML, normally the content is just inlined in the rendered DOM structure. See <h1>{currentWord}</h1>
You may want to look into storing your state in a larger object rather than everything being a separate useState. You may also want to look into useReducer.

setInterval updating state in React but not recognizing when time is 0

I am practicing React useState hooks to make a quiz timer that resets every ten seconds. What I have now is updating the state each second, and the p tag renders accordingly. But when I console.log(seconds) it shows 10 every time, and so the condition (seconds === 0) is never met . In Chrome's React DevTools, the state is updating accordingly as well. What am I doing wrong here?
import React, {useState } from 'react';
function App() {
const [seconds, setSeconds] = useState(10);
const startTimer = () => {
const interval = setInterval(() => {
setSeconds(seconds => seconds - 1);
// Logs 10 every time
console.log(seconds)
// Never meets this condition
if (seconds === 0) {
clearInterval(interval)
}
}, 1000);
}
return (
<div>
<button onClick={() => startTimer()}></button>
// updates with current seconds
<p>{seconds}</p>
</div>
)
}
export default App;
That is because the setSeconds updates the state with a new variable on every tick but the initial reference (seconds === 10) still points to the initial set variable. That is why it stays at 10 => The initial reference.
To get the current value, you have to check it in the setSeconds variable (passed as seconds) like this:
const startTimer = () => {
const interval = setInterval(() => {
setSeconds(seconds => {
if(seconds < 1) {
clearInterval(interval);
return 10;
}
return seconds - 1;
});
}, 1000);
}
Here is a working sandbox
You should also rename the variable to not have the same name (seconds) twice in a single funtion.

Categories

Resources