Home > OS >  React Native timer acting weird (laggy?)
React Native timer acting weird (laggy?)

Time:01-20

I'm trying out React Native by implementing a simple timer. When running the code, the counting works perfectly for the first 6 seconds, where then the app starts to act weird.

Here is the code, which you can try in this Expo Snack

import React, { useEffect, useState } from 'react';
import { StyleSheet, Text, View } from 'react-native';
import { Button } from 'react-native';

const App = () => {


  return (
    <View style={styles.container}>
      <Timer></Timer>
    </View>
  );
}

const Timer = (props) => {
  const [workTime, setWorkTime] = useState({v: new Date()});
  const [counter, setCounter] = useState(0);
  setInterval(() => {
    setCounter( counter   1);
    setWorkTime({v:new Date(counter)});
  }, 1000);
  return (
    <View>
      <Text>{workTime.v.toISOString()}</Text>
    </View>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    alignItems: 'center',
    justifyContent: 'center',
  },
});

export default App;

CodePudding user response:

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

CodePudding user response:

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.

  •  Tags:  
  • Related