Omar Odaini
Omar Odaini

Reputation: 101

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

Upvotes: 1

Views: 280

Answers (2)

Naresh
Naresh

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

Ferin Patel
Ferin Patel

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.

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>

Upvotes: 1

Related Questions