Himanjli Jain
Himanjli Jain

Reputation: 57

console.log when used in setInterval function is running infinite times

import React from "react";

function App() {
  let time = new Date().toLocaleTimeString();

  const [Time, setTime] = React.useState(time);

  function getTime() {
    time = new Date().toLocaleTimeString([], { hour12: false });
    //console.log(time);

    setTime(time);
  }
  setInterval(getTime, 2000);
  return (
    <div className="container">
      <h1>{Time}</h1>
      <button onClick={getTime}>Get Time</button>
    </div>
  );
}

export default App;

This is the App component of React.js. This code is creating a running clock. When I'm commenting out the setInterval function, console.log is working fine, but as soon as I'm enabling that function, everything on the main screen works fine, but on the console screen, console.log(time) is running infinite times. Please help. Thanku.

Upvotes: 0

Views: 1415

Answers (4)

apena
apena

Reputation: 2321

Every state change causes the App to render so you get an endless loop. It is best not to use the useEffect hook with an empty array in your App as useEffect is main way to handle state changes and passing it an empty array makes it fire only once. In comes a custom hook to the rescue. Its easy to make one, just name your custom hook with the prefix 'use', wrap it in a useEffect call and pass it an array (as the second argument) of what the hook depends, in this case that dependency setTime.

Here is a cleaned up version of your App using a custom hook to update the time state:

const App = () => {
  let startTime = new Date().toLocaleTimeString([], { hour12: false });
  const [time, setTime] = React.useState(startTime);
  const useTime = (ms) => React.useEffect( () => setInterval(getTime, ms), [setTime] );
  const getTime = () => setTime( new Date().toLocaleTimeString([], { hour12: false }) );

  useTime(1000);
  
  return (
    <div className="container">
      <h1>{time}</h1>
      <button onClick={getTime}>Get Time</button>
    </div>
  );
}

Lastly clocks usually update once per second but as you can see from the code you can change that by what you pass to useTime.

Upvotes: 0

macborowy
macborowy

Reputation: 1544

Try this:

const { useState, useEffect } = React;

function App() {
  const [time, setTime] = useState(new Date().toLocaleTimeString());

  useEffect(() => {
    const interval = setInterval(setNewTime, 2000);
    
    return () => clearInterval(interval);
  }, [])

  const setNewTime = () => {
    const newTime = new Date().toLocaleTimeString([], { hour12: false });
    setTime(newTime);
  }
  
  const getTime = () => {
    console.log(`Current time is: ${time}`);
  }

  return (
    <div>
      <h1>{`Current time is ${time}`}</h1>
      <p>(updated every 2 seconds)</p>
      <button onClick={getTime}>Get time</button>
    </div>
  );
}

ReactDOM.render(<App />, document.getElementById("root"));
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.8.4/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.8.4/umd/react-dom.production.min.js"></script>
<div id="root"></div>

Upvotes: 2

Prince Agrawal
Prince Agrawal

Reputation: 410

The setInterval calls getTime every 2sec. So, every 2sec getTime prints to the console.

Upvotes: 0

EvanMorrison
EvanMorrison

Reputation: 1217

The problem is that every time the component renders it runs setInterval(getTime, 2000) and every time that happens it updates state, thereby causing a rerender. One approach would be to extract setInterval to a separate custom hook that doesn't run every time App rerenders. You may find this article helpful https://overreacted.io/making-setinterval-declarative-with-react-hooks/.

Upvotes: 5

Related Questions