I'm beginner in React and I want create a component that is in constant motion, incrementing its style property transform:translate
, but the increment isn't what I expected. What's happening?
This is my code:
function Point() {
const [countX, setCountX] = useState(0);
const [countY, setCountY] = useState(0);
setInterval(() => {
setCountX((count) => count 1);
setCountY((count) => count 1);
console.log(countX ":" countY);
}, 500);
const styles = {
transform: `translate(${countX}px,${countY}px)`,
};
return <PointStyle style={styles} />;
}
And the output in the console from console.log(countX ":" countY)
is as follows:
0:0
1:1
0:0
2:2
1:1
4:4
0:0
5:5
2:2
7:7
1:1
9:9
4:4
10:10
11:11
0:0
12:12
5:5
14:14
2:2
7:7
CodePudding user response:
Every time your component re-renders, it is creating an interval. Also, the previous intervals are not getting cleaned up. So, all your intervals are updating the count at the same time.
You need to move the setInterval
into a useEffect
and also cleanup the interval.
useEffect(() => {
const interval = setInterval(() => {
setCountX((count) => count 1);
setCountY((count) => count 1);
console.log(countX ":" countY);
}, 500);
return () => clearInterval(interval)
}, [])
CodePudding user response:
Each time you call one of your setState functions (setCountX
, or setCountY
) the component will end up being rerendered. This will cause the setInterval
to be called repeatedly creating many intervals running out of sync all attempting to update the X and Y position of your point.
To fix this you need to put your setInterval
code inside of a useEffect
.
const Point = () => {
const [countX, setCountX] = useState(0);
const [countY, setCountY] = useState(0);
// The below useEffect will be triggered on component mount and
// whenever a value in the dependency array changes (an empty
// dependency array is only on mount, such as this example).
//
// The function returned from the useEffect is a clean up function
// that will be run whenever a value in the dependency array changes
// or when the component unmounts
useEffect(() => {
// Create interval to change point X and Y, ensuring to store
// the returned ID so that the interval can be cleared later.
const intervalId = setInterval(() => {
setCountX((count) => count 1);
setCountY((count) => count 1);
console.log(countX ":" countY);
}, 500);
// clean up function created in the current closure will
// keep hold of previously stored intervalId value.
return () => {
// Clear interval (in this example only run on component unmount)
clearInterval(intervalId);
}
}, [] /* Dependency Array */)
const styles = {
transform: `translate(${countX}px,${countY}px)`,
};
return <PointStyle style={styles} />;
}