Reputation: 57
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
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
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
Reputation: 410
The setInterval calls getTime every 2sec. So, every 2sec getTime prints to the console.
Upvotes: 0
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