I am working on my personal Portfolio using React and I want to add a statement on the landing page that displays my local time and timezone to recruiters, as seen below:
I have implemented this using the date-fns library but I am currently facing one issue. As time goes by, the time values on the screen stay constant and do not update themselves automatically. As we speak, it is currently 16:58 but the time still shows 16:47. I have to do a manual refresh for the updated time values to show. I want to implement this in such a way that the time will update every 60 seconds and show the current time always.
I wrote this Class component to implement the feature:
export class Time extends Component {
constructor() {
super();
var today = new Date();
var time = format(today, 'HH:mm')
this.state = {
currentTime: time
}
}
render() {
return (
<div className="time">
<p>
My Local Time is { this.state.currentTime } GMT +3
</p>
</div>
);
}
}
setInterval(Time, 60000);
What could I do to make this possible?
I tried using the solution provided Luke-shang-04 but I ran into issues with declaration of the intervalId variable. I therefore resorted to using React Hooks and converted this component into a Functional component.
The code below works:
export const Time = () => {
const [time, setTime] = useState(new Date());
useEffect(
() => {
const intervalId = setInterval(() => {
setTime(new Date());
}, 60000);
return () => {
clearInterval(intervalId)
}
}
)
return(
<div>
<p>{`My local time is ${format(time, 'HH:mm')} GMT+3`} </p>
</div>
)
}
Create a componentDidMount method, and place the setInterval in there. Make sure that the setInterval updates this.state.currentTime. It's also a good idea to clear the interval afterwards, so store the interval id and then call clearInterval in componentWillUnmount.
Something like this
export class Time extends Component {
intervalId
constructor() {
super()
var today = new Date()
var time = format(today, "HH:mm")
this.state = {
currentTime: time,
}
}
componentDidMount() {
var today = new Date()
var time = format(today, "HH:mm")
intervalId = setInterval(() =>
this.setState({
currentTime: time,
}),
60000
)
}
componentWillUnmount() {
clearInterval(intervalId) // Clear interval to prevent memory leaks
}
render() {
return (
<div className="time">
<p>My Local Time is {this.state.currentTime} GMT +3</p>
</div>
)
}
}
I would suggest that you read up on the React Docs, since these are the basics of React.
Related
First I'll say that I've tried the solution in How do I use .toLocaleTimeString() without displaying seconds?
and it didn't work for me.
I am trying to create a timer component with the h:m:s format but I always see the full current date. Does anyone have a solution?
Here is my code:
import React , { Component } from 'react';
class Timer extends Component {
constructor (props) {
super(props);
this.state = {
count : 0
}
}
render () {
{
var hour = Math.floor(this.state.count/3600),
minutes = Math.floor((this.state.count - hour*3600)/60),
seconds = (this.state.count - hour*3600 -minutes*60 ),
date = new Date();
date.setHours(hour, minutes, seconds);
}
return(
<div>
{date.toString( {hour: '2-digit', minute:'2-digit'})}
</div>
);
}
}
export default Timer;
What I'm getting from this component looks like this:
Is it related to how react-js is working or am I writing the javascript code wrong?
you can do it like this:
//your date
let temp = new Date();
let HMS = `${temp.getHours()}:${temp.getMinutes()}:${temp.getSeconds()}`
and now you have your hours:minutes:seconds
I'm trying to make a timer that will start (at 00:00) when the user loads the Home page, increment every second, and reset to 00:00 when they go to a different page.
Here's my newest code:
const [ timerCount, increaseTimer ] = useState(0);
function timerItself() {
function timerIncrement() {
var currentTimerCount = timerCount;
increaseTimer(currentTimerCount + 1);
};
setInterval(() => timerIncrement, 1000);
clearInterval(timerIncrement);
return;
}
function pageHome() {
var timerMinutes = JSON.stringify(Math.floor(timerItself / 60));
var timerSeconds = JSON.stringify(timerItself % 60);
timerItself();
return (
<div classname="App">
<header className="App-header">
<h1>Hello world!</h1>
<img src={logo} className="App-logo" alt="logo" />
<p>{timerMinutes.padStart(2, '0')}:{timerSeconds.padStart(2, '0')}</p>
<p>
<button onClick={(() => changePage('About'))}>
About
</button>
<button onClick={(() => changePage('Help'))}>
Help
</button>
</p>
</header>
</div>
);
}
Previously I had wrapped the setInterval() in a const like so:
const timer = setInterval(() => increaseTimer(timerCount + 1), 1000); and tried putting clearInterval() in the buttons' onClick functions thinking this would just reset the timer.
The output had weird behaviour: the timer would count up at irregular intervals, starting very slow from 00:03 instead of 00:00 and becoming very fast. The sequence of times shown was consistent but I couldn't recognise it as a mathematical sequence: 3, 7, 11, 29... 81... and reaching very high numbers within about 30 seconds. Couldn't figure out where in the code the numbers were coming from.
I did some more reading and came to the conclusion that timer was setting off more than one timer at the same time and somehow adding their outputs together. So I wrote timerItself() instead, with a clearInterval() in there which I assumed would also fire every second to clear the old timer and clean up for the new one. But now there's no counting going on at all, and the stringified output from the timer is "null:null".
I'm confused over where the clearInterval() should even go, as I think that's the main key to this problem. I'd really appreciate any help.
To create an interval that start when you render a component, you can use useEffect Hook, with your timerCount as a dependency. Because when you first render the component you want to start the counter, and as you increment the timer you want the interval to keep going.
import React, { useState, useEffect } from 'react';
export default function Home() {
const [timerCount, increaseTimer] = useState(0);
useEffect(() => {
let myTimerId;
myTimerId = setInterval(() => {
increaseTimer(timerCount + 1);
}, 1000);
return () => clearInterval(myTimerId);
}, [timerCount]);
return (
<div>
<h1>Timer {timerCount}</h1>
</div>
);
}
export function Other() {
return <div>This is the other Page</div>;
}
The arrow function returned by the useEffect Hook is a cleanup function that will clearTheInterval to prevent a memory leak.
And here is the App.js component
import React from 'react';
import { BrowserRouter as Router, Link, Route, Switch } from 'react-router-dom';
import Home, { Other } from './Home';
export default function App() {
return (
<>
<Router>
<ul>
<li>
<Link to="/home">Home</Link>
</li>
<li>
<Link to="/other">Other</Link>
</li>
</ul>
<Switch>
<Route exact path="/home" component={Home} />
<Route exact path="/other" component={Other} />
</Switch>
</Router>
</>
);
}
I used a library called react-router-dom to navigate between paths on your app, without making a new request.
Reference : https://reactrouter.com/web/guides/quick-start
And
useEffect : https://reactjs.org/docs/hooks-effect.html
// Also I suggest you to name the function that updates the state in such manner :
const [myExampleState, setMyExampleState] = useState('Example1');
setMyExampleState('Example2');
Hope I helped. ( Also check out the lifecycle to understand better how React works ;D )
Yeah setinterval doesn't play well with hooks. Even with the useEffect solution you'll lose time. If the interval is up to 3/4ths of a second, a rerender for any reason will destroy and recreate the timer back at zero again. Lots of rerenders, spaced out, effectively paralyze the timer.
This might be a good case to put the timer outside of the component. It's still in scope for the component without being controlled by it. Same for whatever function that's called on nav away.
Solved!
This code is working well for my purposes:
const [ timer, changeTimer ] = useState(0);
var currentTimerCount = timer;
useEffect(() => {
if (page !== 'Home')
changeTimer(0);
if (page == 'Home')
currentTimerCount = setInterval(() => {changeTimer(prevTimer => prevTimer + 1)}, 1000);
return () => clearInterval(currentTimerCount);
});
var timerMinutes = JSON.stringify(Math.floor(currentTimerCount / 60));
var timerSeconds = JSON.stringify(currentTimerCount % 60);
and then calling timerMinutes and timerSeconds as before.
Thanks to everyone who answered!
I am trying to make a countdown clock in my mobile view. But I can't seem to make the numbers work, my wrapper doesn't connects.
Hope somebody can see what I should do. I'm pretty new to Javascript and it all.
Code:
import React, { Component, Fragment, useState } from "react";
const MobileView = () => {
class wrapper extends Component {
constructor() {
super();
this.state = {};
}
componentDidMount() {
this.startInterval();
}
startInterval = () => {
const second = 1000;
const minute = second * 60;
const hour = minute * 60;
const day = hour * 24;
let x = setInterval(function () {
document.getElementById("days").innerText = "00";
}, second);
};
}
return (
<div className="wrapper">
<header className="Appheader">
<h1 id="head">Countdown</h1>
</header>
<div id="list">
<ul>
<li>
<span id="days"></span>Days
</li>
<li>
<span id="hours"></span>Hours
</li>
<li>
<span></span>Mins
</li>
<li>
<span></span>Seconds
</li>
</ul>
</div>
</div>
);
};
export default MobileView;
You seem to be mixing some concept:
You are are creating a class component, that you do not use, inside a functional component
You are trying to update the DOM directly instead of using the state
Here are the step you need to follow to make it work:
Choose between a class and a functional component
Setup your state accordingly
Start an interval that will update the state every X second
Clear the interval when the component is unmounted
Use your state value in the render function
Here is an working counter using a functional component, I will let you implement the logic for days, hours and minutes.
import React, { useEffect, useState } from "react";
// 1. Choose between a class and a functional component
export const MobileView = () => {
// 2. Setup your state accordingly
const [secondsElapsed, setSecondsElapsed] = useState(0);
const timer = () => {
setSecondsElapsed(secondsElapsed + 1);
};
useEffect(() => {
// 3. Start an interval that will update the state every X second
const interval = setInterval(timer, 1000);
// 4. Clear the interval when the component is unmounted
return () => clearInterval(interval);
});
return (
<div className="wrapper">
<header className="Appheader">
<h1 id="head">Countdown</h1>
</header>
<div id="list">
<ul>
<li>
<span></span>
{secondsElapsed} // 5. Use your state value in the render function
</li>
</ul>
</div>
</div>
);
};
also, I recommend to add useCallback and dependencies to the useEffect
const timer = useCallback(() => {
setSecondsElapsed(secondsElapsed => secondsElapsed + 1);
}, []);
useEffect(() => {
// 3. Start an interval that will update the state every X second
const interval = setInterval(timer, 1000);
// 4. Clear the interval when the component is unmounted
return () => clearInterval(interval);
}, [timer]);
I'm trying to periodically run a calculation (every 5 seconds) and update a component's state with the calculated value using a setInterval timer. What I've seen is that the updateCalculation() function does get called every 5 seconds but when monitoring the memory usage using the Chrome devtools it just keeps on growing endlessly on every call by setInterval. The memory never seems to get released.
Snapshot 1:
Snapshot 2:
What could be a possible workaround for running calculations periodically?
I'm still pretty new to React and am really not sure what I'm doing wrong.
class MyComponent extends React.PureComponent {
constructor(props) {
super(props);
this.state = {
calculated: []
};
}
componentDidMount() {
this.calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
}
componentWillUnmount() {
clearInterval(this.calculationUpdater);
}
// Memory leak here
// The function that gets called by setInterval to calculate data and update the state
updateCalculation() {
let data = [];
for (let i = 0; i < 60000; i++) {
data.push({x: i, y: i, z: i});
}
this.setState({
calculated: data
});
}
render() {
return (
<React.Fragment>
<Child calc={this.state.calculated} />
</React.Fragment>
);
}
}
I'm not doing anything special with the Child component at the moment. This is what it looks like:
class Child extends React.PureComponent {
render() {
return (
<div></div>
);
}
}
EDIT: Check following post: Does JavaScript setInterval() method cause memory leak?
You are not clearing the interval because you are are not setting or reading state correctly. So if your component keep getting mounted and unmounted, you set a new interval but do not clear the interval on unmount.
this.calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
This should be
const calculationUpdater = setInterval(() => this.updateCalculation(), 5000);
console.log(calculationUpdater )
this.setState({calculationUpdater : calculationUpdater})
And you access the state as following:
console.log(this.state.calculationUpdater);
clearInterval(this.state.calculationUpdater);
The only thing I can suggest is to try switching from PureComponents to Components; I noticed my initial snapshot was laggier for PureComponents, though either way my results were not like yours.
This is my mixin in Vue.js:
var myMixin = {
data () {
clockInt: '',
clock: '',
currentTime: new Date()
},
mounted () {
this.intervalSetup();
},
methods: {
intervalSetup () {
var _this = this;
// init
this.getTime();
// start interval every 60s
this.clockInt = setInterval(
_this.getTime(), 60000
);
},
getTime () {
var now = this.currentTime,
h = now.getHours(),
m = now.getMinutes();
this.clock = h + ':' + m;
}
}
};
This should display a simple digital watch which sets the time every 60s.
Then, I register this mixin to Vue, require my component and start a new instance of Vue:
Vue.component('comp', require('./components/MyComponent.vue'));
Vue.mixin(myMixin);
const app = new Vue({
el: '#app'
});
My component looks like this:
<template>
<div>{{ clock }}</div>
</template>
<script>
export default {
data () {
return {
someData: []
}
},
mounted () {
// call methods
},
methods: {
// some methods
}
};
</script>
The first init of the mixin method works fine and as I can see in the Vue dev-tool, all data is present too. But it does not refresh the data in the interval I set up.
Hopefully someone had similar issues and can help me with this.
Well guys. Both solution work exactly as expected - as a single component and as a mixin. Good to know.
The issue was currentTime: new Date(). The date time was declared in data object only once. So the interval always took this timestamp.
I just removed this and changed var now = new Date(); in my mixin's method.
Thank you guys!