Reputation: 101
I have this Stopwatch component which has a wired behavior: the stopwatch works perfectly except it stops re-rendering in UI when I comment the line setTest(val => val + 1)
from inside useEffect(). I validated that timeArray
still gets updated with console.log but the <Text>
in UI doesn't. Please help if you notice something not logically correct with the code below:
const Stopwatch = () => {
const [timeArray, setTime] = useState([0,0,0])
const [test, setTest] = useState(0)
useEffect(() => {
const interval = setInterval(() => {
setTime(val => time(val))
setTest(val => val + 1) <-- commenting this line breaks UI re-rendering below of var timeArray!!
}, 1000)
return () => clearInterval(interval)
},[])
return(
<View>
<Text>{timeArray[2]}:{timeArray[1]}:{timeArray[0]}</Text>
</View>
)
}
const time = (value) =>{
value[0]++
if (value[0] == 59){
value[1]++
value[0] = 0
}
if (value[1] == 59 && value[0] == 59){
value[2]++
value[0] = 0
value[1] = 0
}
return value
}
export default Stopwatch
Upvotes: 1
Views: 280
Reputation: 1261
your UI is not being updated because you are just updating the values in array but the reference of the array is still the same so In order for the ui to re-render you have to assign the new array.
useEffect(() => {
const interval = setInterval(() => {
const t = time(timeArray);
setTime([...t]);// creating new array and setting to the state
// setTime(t);// assigning the same array. rerender won't happen
}, 1000);
return () => clearInterval(interval);
}, []);
Upvotes: 2
Reputation: 3998
Value returning from time function is setting up the new state for setTime. So to perform state update, you have to spread the values into setTime state.
import React, { useState, useEffect } from "react";
import ReactDOM from "react-dom";
function App() {
const [timeArray, setTime] = useState([0, 0, 0]);
const [test, setTest] = useState(0);
const time = value => {
value[0]++;
if (value[0] === 59) {
value[1]++;
value[0] = 0;
}
if (value[1] === 59 && value[0] === 59) {
value[2]++;
value[0] = 0;
value[1] = 0;
}
return value;
};
useEffect(() => {
const interval = setInterval(() => {
const data = time(timeArray);
setTime([...data]);
//setTest(val => val + 1)
}, 1000);
return () => clearInterval(interval);
}, [timeArray]);
return (
<div className="App">
<h1>Hello CodeSandbox</h1>
<div>
<p>
{timeArray[2]}:{timeArray[1]}:{timeArray[0]}
</p>
</div>
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(
<React.StrictMode>
<App />
</React.StrictMode>,
rootElement
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.0/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.0/umd/react-dom.production.min.js"></script>
<div id="root"></div>
Upvotes: 1