Search code examples
reactjsreact-nativeuse-effect

react-native's useEffect Bug or logical error?


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

Solution

  • 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.

    CodeSandBox link Here

    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>