In the code snippet below, I add a random number to an array every 3 seconds using setInterval. This goes well, until I try to also call the function on the first render (see the commented line). This gives me this error: Maximum update depth exceeded.
const [listItems, setListItems] = useState([]);
useEffect(() => {
function extendTheList() {
const randNr = Math.floor(Math.random() * 10);
setListItems([...listItems, randNr]);
}
// extendTheList();
const int = setInterval(() => {
extendTheList();
}, 3000);
return () => clearInterval(int);
}, [listItems]);
Sandbox: https://codesandbox.io/s/vigilant-shamir-ltkh6m?file=/src/App.js
Remove the dependency to avoid infinite loop
const [listItems, setListItems] = useState([]);
useEffect(() => {
function extendTheList() {
const randNr = Math.floor(Math.random() * 10);
setListItems(listItems => [...listItems, randNr]);
}
extendTheList();
const int = setInterval(() => {
extendTheList();
}, 3000);
return () => clearInterval(int);
}, []);
https://codesandbox.io/s/goofy-stallman-e1m4wo
You have listItems in the dependency array of useEffect which will retrigger the useEffect every time you change listItems.
If you want to use the old value of the state use the function version of setState
const [listItems, setListItems] = useState([]);
useEffect(() => {
function extendTheList() {
const randNr = Math.floor(Math.random() * 10);
setListItems((currentItems) => [...currentItems, randNr]);
}
// extendTheList();
const int = setInterval(() => {
extendTheList();
}, 3000);
return () => clearInterval(int);
}, [setListItems]);
Related
I need to make background change every second. if i use setinterval. the background changes too fast.
here's my code:
const { url, id, isButtonPrev, isButtonNext } = useOwnSelector(state => state.sliderReducer);
const img = useRef<HTMLImageElement>(null);
const dispatch = useOwnDispatch();
Here's function which chang background
const setBackGround = (index: number | null = null) => {
console.log(index)
if(img.current) {
img.current.src = `${url}${id < 10 ? `0${id}` : `${id}`}.jpg`;
img.current.onload = () => {
document.body.style.backgroundImage = `url(${img.current?.src})`;
if (index) dispatch(setId(index));
dispatch(isButton(''));
}
}
}
then I call this function:
setBackGround();
setInterval(() => {
setBackGround(id + 1);
}, 1000);
but background change very fast
I also tried to use the useEffect hook. But it didn’t help either
useEffect( () => {
const intervalID = setInterval(() => {
setBackGround(id + 1);
}, 1000);
return clearInterval(intervalID);
}, []);
useRef returns an object like {current: "value"}.Therefore, you need to use it as follows.
const imgRef = useRef<HTMLImageElement>(null);
if(imgRef.current){
imgRef.current.src = url;
}
I am a beginner using useEffect in React to update the index state of an array on an interval. I want the index to increase by 1 every five seconds, and when it reaches the end of the array loop back to 0.
I put an 'if' statement to check for this within my useEffect function, but it doesn't seem to be firing. When the array reaches the end, my console shows an error reading from an undefined index.
const [idx, setIdx] = useState(0);
useEffect(() => {
const interval = setInterval(() => setIdx((previousValue) => previousValue
+1), 5000);
if(idx > testimonials.length-1) { setIdx(0)};
return () => {
clearInterval(interval);
};
}, []);
Can anyone see what I'm doing wrong?
Use the remainder operator (%):
const [idx, setIdx] = useState(0);
useEffect(() => {
const interval = setInterval(
() => setIdx(idx => (idx + 1) % testimonials.length),
5000,
);
return () => clearInterval(interval);
}, []);
You need to pass the idx value to the useEffect dependencies array.
import React from 'react';
const testimonials = [1, 2, 3, 4, 5, 6];
export default function App() {
const [idx, setIdx] = React.useState(0);
React.useEffect(() => {
const interval = setInterval(
() => setIdx((previousValue) => previousValue + 1),
1000
);
if (idx > testimonials.length - 1) {
setIdx(0);
}
return () => {
clearInterval(interval);
};
}, [idx]);
return (
<div>
<h1>{idx}</h1>
</div>
);
}
you should use this statement
if(idx > testimonials.length-1) { setIdx(0)};
outside the useEffect and above the return which is returning the JSX.
The Idea is - I press 'start timer' and a function sets the variable 'sessionSeconds' based on whether its work 25mins or rest 5mins session (true/false). Problem is , when interval restarts, function doesn't update this 'currentSessionSeconds'. Maybe it's something with lifecycles or I should use useEffect somehow..
const TimeTracker = () => {
const [work, setWork] = useState(25);
const [rest, setRest] = useState(5);
const [remainingTime,setRemainingTime]= useState(25);
const [iswork, setIswork] = useState(true);
function startTimer() {
clearInterval(interval);
let sessionSeconds = iswork? work * 60 : rest * 60
interval = setInterval(() => {
sessionSeconds--
if (duration <= 0) {
setIswork((iswork) => !iswork)
updateTime(sessionSeconds)
startTimer();
}
}, 1000);
function updateTime(seconds){
setRemainingTime(seconds)
}
}
return (
<div>
<p>
{remainingTime}
</p>
<button onClick={startTimer}>timer</button>
</div>
);
}
I didnt include other code for converting, etc to not over clutter.
I couldn't get your code working to test it since it is missing a }.
I changed your code to include use effect and everything seems to work fine.
https://codesandbox.io/s/wizardly-saha-0dxde?file=/src/App.js
const TimeTracker = () => {
/* In your code you used use state however you didn't change
the state of these variables so I set them to constants.
You can also pass them through props.
*/
const work = 25;
const rest = 5;
const [remainingTime, setRemainingTime] = useState(work * 60);
const [isWork, setIsWork] = useState(true);
const [isTimerActive, setIsTimerActive] = useState(false);
const startTimerHandler = () => {
isWork ? setRemainingTime(work * 60) : setRemainingTime(rest * 60);
setIsTimerActive(true);
};
useEffect(() => {
if (!isTimerActive) return;
if (remainingTime === 0) {
setIsWork((prevState) => !prevState);
setIsTimerActive(false);
}
const timeOut = setTimeout(() => {
setRemainingTime((prevState) => prevState - 1);
}, 1000);
/* The return function will be called before each useEffect
after the first one and will clear previous timeout
*/
return () => {
clearTimeout(timeOut);
};
}, [remainingTime, isTimerActive]);
return (
<div>
<p>{remainingTime}</p>
<button onClick={startTimerHandler}>timer</button>
</div>
);
};
I have a function that sets a reminder to pop up on the screen, but the message wont go away. Am I using the clearInterval with react hooks correctly in this function?
useEffect(() => {
const interval = setInterval(() => {
handleReminder(activeReminders);
}, 1000);
return () => clearInterval(interval);
}, [activeReminders]);
useEffect(() => {
const notesWithReminder = getNotesWithReminder(notes);
if (notesWithReminder.length > 0) {
setActiveReminders(notesWithReminder);
}
}, [notes]);
function getNotesWithReminder(notes) {
return notes.filter((note) => note.reminder && !note.isReminderShow);
}
function handleReminder(reminders) {
const activeRem = reminders.find((rem) => {
const now = Date.now();
const getRemTime = new Date(rem.reminder).getTime();
return getRemTime <= now;
});
setActiveReminder(activeRem);
setShowNotifyModal(true);
}
Message was not dismissing due to if statement which created a memory leak.
solution:
useEffect(() => {
const notesWithReminder = getNotesWithReminder(notes);
setActiveReminders(notesWithReminder);
}, [notes]);
I'm loading multiple animals into my ThreeJS project. All these animals have PositionalAudio with a setInterval function. I use them inside a useEffect function. On the callback I want to clear the interval, but it keeps calling the function.
This is the function where I set my setInterval:
const loadAudio = () => {
const animalSound = new THREE.PositionalAudio(listener);
animalSound.setBuffer(animalBuffer);
playSounds = setInterval(() => {
animalSound.play();
} , 5000);
audios.push(animalSound);
}
In the return function I try to clear the interval:
return () => {
audios.forEach((audio) => {
audio.stop();
clearInterval(playSounds);
});
};
Sadly the audio keeps playing every 5 seconds
Here is a code snippet
https://codesandbox.io/s/bitter-tree-bb4ld?file=/src/App.js
According to your code snippet, say you have Button:
<button
onClick={buttonToggle}
>
{start ? 'start' : 'stop'}
</button>
Initially we have some setup for useState and handle click function
const [seconds, setSeconds] = useState(0);
const [btnStart, setBtnStart] = useState(true);
const buttonToggle = useCallback(
() => setBtnStart(run => !run)
, []);
In the useEffect you will do following changes
useEffect(() => {
if(!btnStart) {
// setSeconds(0); // if you want to reset it as well
return;
}
const interval = setInterval(() => {
setSeconds(seconds => seconds + 1);
}, 1000);
return () => clearInterval(interval);
}, [btnStart]);