Reputation: 71
I am trying to use setTimeout to flip a useState between true and false every 6 seconds. I use this state to add/ remove a class from a div - this will cause the div to toggle between top: 0
to top: 100%
(and a transition
takes care of the animation).
To be more specific, I have an iPhone wrapper image with a content image inside it. The idea is that it will slowly scroll to the bottom of the content image, and then after 6 seconds (from the time it started scrolling down), it will then start scrolling back up, ad infinitum. However, it's not working right at all.
I've tested it with an onClick and it works exactly as I intend. However, with the setTimeout logic:
Of course, it cannot be true and false, and it should be flipping its value. Could someone tell me why it's not working and perhaps tell me why it's acting in this bizarre way?
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [isInitialised, setInitialised] = useState(false)
const [animating, setAnimating] = useState(false)
const startAnimation = () => {
setAnimating(!animating); /* Even if I use `true`, it will log to the console as `false` */
console.warn('animation change iphone!');
console.warn(animating);
console.warn(isInitialised); /* This also always logs as `false` */
setTimeout(() => {
startAnimation();
}, 6000);
}
if (!isInitialised) {
setInitialised(true);
startAnimation();
}
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
I use the isInitialised
otherwise it seems to hit an infinite loop.
Upvotes: 1
Views: 289
Reputation: 6869
You can do something like this. useEffect with empty array as deps so it will only once, you don't need isInitialized state.
In useEffect use setInterval so it will run every 6 seconds.
use callback way to setting state so you always get the correct value of animating.
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [animating, setAnimating] = useState(false)
useEffect(() => {
const startAnimation = () => {
setAnimating(v => !v);
}
const interval = setInterval(() => {
startAnimation();
}, 6000);
return () => clearInterval(interval);
}, []);
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
Upvotes: 2
Reputation: 3117
you can use useEffect and bring setTimeout outside.
import React, { useState } from 'react';
import iphone from '../Images/iphone.png'
const Iphone = (props) => {
const [isInitialised, setInitialised] = useState(false)
const [animating, setAnimating] = useState(false)
useEffect(()=>{
startAnimation();
},[]);
const startAnimation = () => {
setAnimating(!animating);
console.warn('animation change iphone!');
console.warn(animating);
console.warn(isInitialised);
}
setTimeout(() => {
if (!isInitialised) {
setInitialised(true);
startAnimation();
}
}, 6000);
return (
<div className={`iphone align-mobile-center ${animating ? "iphone--animating" : ""}`} onClick={() => setAnimating(!animating)}>
<img className="iphone__image" src={iphone} alt="An iPhone" />
<div className="iphone__content">
<img className="iphone__content-image" src={props.image} alt={props.alt} />
</div>
</div>
)
}
export default Iphone;
Upvotes: 1