React Native timer acting weird (laggy?)

Solution 1:

For each new component rerender, React create a new interval, which results in a memory leak and unexpected behavior.

Let us create a custom hook for interval

import { useEffect, useLayoutEffect, useRef } from 'react'

function useInterval(callback, delay) {
  const savedCallback = useRef(callback)

  // Remember the latest callback if it changes.
  useLayoutEffect(() => {
    savedCallback.current = callback
  }, [callback])

  // Set up the interval.
  useEffect(() => {
    // Don't schedule if no delay is specified.
    // Note: 0 is a valid value for delay.
    if (!delay && delay !== 0) {
      return
    }

    const id = setInterval(() => savedCallback.current(), delay)

    return () => clearInterval(id)
  }, [delay])
}

export default useInterval


Use it in functional component

const Timer = (props) => {
  const [workTime, setWorkTime] = useState({v: new Date()});
  const [counter, setCounter] = useState(0);
  
  useInterval(()=>{  
    setCounter( counter + 1);
    setWorkTime({v:new Date(counter)});
},1000)


  return (
    <View>
      <Text>{workTime.v.toISOString()}</Text>
    </View>
  )
}

Here tested result - https://snack.expo.dev/@emmbyiringiro/58cf4b

Solution 2:

As @AKX commented, try wrapping your interval code in a useEffect. Also, return a function from it that clears the interval (the cleanup function).

  useEffect(() => {
    const interval = setInterval(() => {
      setCounter( counter + 1);
      setWorkTime({v:new Date(counter)});
    }, 1000);
    return () => clearInterval(interval);
  });

However, using setInterval with 1000 second delay does not yield an accurate clock, if that's what you're going for.