Where to apply my moment() function in a react component? - javascript

I am trying to do a clock component, simply to give the date and time in local format in a webpage. I imported MomentJS using the command line npm i moment --save in my webpack environment. Next I wrote this in my Clock.jsx component (mostly based on the React example on the website).
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now()
};
}
tick = () => {
this.setState({dateTimestamp: this.state.dateTimestamp + 1});
console.log('tick');
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const date = this.state.dateTimestamp;
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}
Doing this the timestamp incremented correctly. However, when passing a new state element in the object, the first value (based on Date.now() ) is calculated in the constructor but for each tick, only the timestamp is incrementing the formatted date is stuck on its first value. Here is the code.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now(),
dateFormatted : Moment(Date.now()).toString()
};
}
tick = () => {
this.setState({dateTimestamp: this.state.dateTimestamp + 1});
console.log(this.state.dateTimestamp);
this.setState({dateFormatted: Moment(this.state.dateTimestamp).toString()});
console.log(this.state.dateFormatted);
}
...
render() {
const date = this.state.dateFormatted;
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}
Does anyone could explain help me solving this issue but above all tell me what is going wrong with my piece of code?
Thanks
UPDATE: In the end my use of moment was not appropriate, even if I cannot figure out why it would not work this way. Find below my correct implementation to have the date and time refreshed every seconds.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateFormatted : Moment().locale('fr').format('dddd Do MMMM YYYY HH:mm:ss').toString()
};
}
tick = () => {
this.setState({
dateFormatted : Moment().locale('fr').format('dddd Do MMMM YYYY HH:mm:ss').toString()
});
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
const date = this.state.dateFormatted;
return(
<div className="clock"> Date (locale) : {date}</div>
);
}
}
This also "solves" the anti pattern issue exposed below (different cross-dependant setState() call). I would need the timestamp for any other reason but I will find a workaround.

#KrzysztofSztompka is correct, but I would add that maintaining two separate state variables to represent the current date as a number and as a formatted string is an antipattern. Derived state variables (i.e., state variables that can be calculated using another state variable) increases the responsibility on the developer to always keep the two state variables in sync. That may not seem too difficult in this simple example, but it can become more difficult in larger, more complicated components/apps. Instead, it is generally considered better practice to maintain one source of truth and calculate any derived values on the fly as you need them. Here's how I would apply this pattern to your example.
import React from 'react';
import Moment from 'moment';
export default class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
dateTimestamp : Date.now()
};
this.tick = this.tick.bind(this);
}
tick() {
this.setState({
dateTimestamp: this.state.dateTimestamp + 1
});
}
componentDidMount() {
this.interval = setInterval(this.tick, 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
render() {
// Calculate the formatted date on the fly
const date = Moment(this.state.dateTimestamp).toString();
return(
<div className="clock"> Heure locale : {date}</div>
);
}
}

change your tick function to this:
tick = () => {
var timestamp = this.state.dateTimestamp + 1;
this.setState({
dateTimestamp: timestamp,
dateFormatted: Moment(timestamp).toString()
});
}
This is because from docs :
setState() does not immediately mutate this.state but creates a
pending state transition. Accessing this.state after calling this
method can potentially return the existing value.
Therefore in your next setState call it use old value. My proposition change this two values at once.

Related

I made a clock class in JavaScript, but it's not working properly when imported. I can only get the seconds and the interval doesn't seem to work too

import React, { Component } from 'react';
import { Text, View } from 'react-native';
export default class Clock extends Component {
componentDidMount(){
setInterval(() => (
this.setState(
{ curHours : new Date().getHours()}
),
this.setState(
{ curMins : new Date().getMinutes()}
),
this.setState(
{ curSeconds : new Date().getSeconds()}
)
), 1000);
}
state = {curHours:new Date().getHours()};
state = {curMins:new Date().getMinutes()};
state = {curSeconds:new Date().getSeconds()};
renderHours() {
return (
<Text>{'Hours:'}{this.state.curHours}</Text>
);
}
renderMinutes() {
return (
<Text>{'Minutes:'}{this.state.curMinutes}</Text>
);
}
renderSeconds() {
return (
<Text>{'Seconds:'}{this.state.curSeconds}</Text>
);
}
}
-I'm trying to make an app that can keep track of time kinda like a daily planner. So I need to get the current time in real time during app run time. The app is supposed to tell the user that they have failed to accomplish a certain task in a given time for example. I tried exporting the clock.js and using its functions but only the renderSeconds() is working, the others are only showing blanks.
I think functional components will be much simpler to solve this, but it's just my opinion.
Here's a link to an example
When you're defining state three times, only the last one is persisted because you're overwriting the previous variable. In addition, your initial state should be declared in a constructor. Add this to the top of your class
constructor() {
this.state = {
curHours:new Date().getHours(),
curMins:new Date().getMinutes(),
curSeconds:new Date().getSeconds(),
}
}

React : state object property is not updating

I am trying to update my my state but in meantime when I console updated state value in console it give me a message that your state value is undefined . could someon eplease help me how to solve this problem . I am using rc-time-picker for picking a time and storing a selected time in localstorage but it did not updating state value
Thanks
Code
import 'rc-time-picker/assets/index.css';
import React, { Component } from 'react';
import TimePicker from 'rc-time-picker';
import moment from 'moment';
const format = 'h:mm a';
const now = moment().hour(0).minute(0);
class Time extends Component {
constructor(){
super();
this.state={
value : ''
}
}
newHandleChange=(value)=>{
this.setState({
value: localStorage.setItem("currentTime",value.format('h:mm a'))
})
}
render(){
console.log("state data",this.state.value)
return(
<div>
<TimePicker
showSecond={false}
defaultValue={now}
className="xxx"
onChange={this.newHandleChange}
format={format}
use12Hours
inputReadOnly
fieldName="time"
/>
</div>
)
}
}
export default Time;
The reason why you're getting undefined is because you're setting new state with the value that gets returned from calling localStorage.setItem, which is undefined.
Here's what you need to do instead:
class Time extends Component {
constructor(props) {
super(props)
this.state = { value: '' }
}
// ...
newHandleChange = (value) => {
const time = value.format('h:mm a')
localStorage.setItem('currentTime', time)
this.setState({
value: time
})
}
}
This way, you're saving your new time into a variable and using it to update currentValue in localStorage and state in your component.
When initializing state in constructor you should pass props to super:
constructor(props){
super(props);
this.state={
value : ''
}
}
The undefined you're getting is just the result from getItem()
You need to set the proper value to the state, please check below code
newHandleChange=(value)=>{
localStorage.setItem("currentTime",value.format('h:mm a')) // if you really needs the local storage.
this.setState({
value: value.format('h:mm a')
})
}
try to used this `
newHandleChange =val=> {
const value=localStorage.setItem('currentTime', val.format('h:mm a'))
this.setState({
value
})
}`
look to this this app https://codesandbox.io/s/competent-bohr-6jon1

Modifying state object directly re rendering the component in ReactJS

I was going through ReactJS documentation. I came across following concept in State and lifecycle section which says
Do Not Modify State Directly For example, this will not re-render a
component:
// Wrong
this.state.comment = 'Hello';
https://reactjs.org/docs/state-and-lifecycle.html
I tried to implement the same behavior and saw that the component got re rendered
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
time : new Date(),
note: "Time is-"
}
}
componentDidMount() {
this.timerId = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearTimer(this.timerId);
}
render() {
return <h1>{this.state.note}{this.state.time.toLocaleTimeString()}</h1>
}
tick() {
this.state.note = "Dude!! Time is";
this.setState({
time : new Date()
})
}
}
ReactDOM.render(
<Clock/>,
document.getElementById('root')
);
The text got re rendered to "Dude Time is" from "Time is"
Can someone explain this? This behavior goes against what react documentation says
This is working because you are also doing the setState after this.state.note = "Dude!! Time is". If you remove the setState call after this line, the example won't work.
Here is the link to the codesandbox. I've removed the setState call.
https://codesandbox.io/s/50r500j62p

Have been using React, how to setTimeout or create a Data Carousel like thing which shows new data from a graphql api every couple of mins?

querying data from a graphql api, storing it in an array, now have to display it using setTimeout or interval ( imagine a data carousel which changes automatically after a specific time) how do i achieve it?
It's important to make sure to clear your interval when your component un-mounts. Otherwise, you'll end up with a nasty set of error messages mentioning how you are setting state on an "unmounted" component.
import React, { Component } from 'react'
class Carousel extends Component {
constructor(props) {
super(props);
this.state = {
intervalId = 0,
updatedProps: {}
}
}
componentDidMount() {
let self = this;
let id = setInterval(function() {
// GRAPHQL => data
this.setState({updatedProps: data, intervalId: id})
})
}
componentWillUnMount() {
clearInterval(this.state.intervalId)
}
render() {
return (
<div>
<Carousel
props={this.state.updatedProps}
/>
</div>
)
}
}
Here it is:
this.setState is the key to your question
setInterval(() => {
call.to.api(data => {
this.setState({graphql_data});
// this will update your ui, as soon as state is set
})
},miliseconds)

Update React component every second

I have been playing around with React and have the following time component that just renders Date.now() to the screen:
import React, { Component } from 'react';
class TimeComponent extends Component {
constructor(props){
super(props);
this.state = { time: Date.now() };
}
render(){
return(
<div> { this.state.time } </div>
);
}
componentDidMount() {
console.log("TimeComponent Mounted...")
}
}
export default TimeComponent;
What would be the best way to get this component to update every second to re-draw the time from a React perspective?
You need to use setInterval to trigger the change, but you also need to clear the timer when the component unmounts to prevent it leaving errors and leaking memory:
componentDidMount() {
this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
#Waisky suggested:
You need to use setInterval to trigger the change, but you also need to clear the timer when the component unmounts to prevent it leaving errors and leaking memory:
If you'd like to do the same thing, using Hooks:
const [time, setTime] = useState(Date.now());
useEffect(() => {
const interval = setInterval(() => setTime(Date.now()), 1000);
return () => {
clearInterval(interval);
};
}, []);
Regarding the comments:
You don't need to pass anything inside []. If you pass time in the brackets, it means run the effect every time the value of time changes, i.e., it invokes a new setInterval every time, time changes, which is not what we're looking for. We want to only invoke setInterval once when the component gets mounted and then setInterval calls setTime(Date.now()) every 1000 seconds. Finally, we invoke clearInterval when the component is unmounted.
Note that the component gets updated, based on how you've used time in it, every time the value of time changes. That has nothing to do with putting time in [] of useEffect.
The following code is a modified example from React.js website.
Original code is available here: https://reactjs.org/#a-simple-component
class Timer extends React.Component {
constructor(props) {
super(props);
this.state = {
seconds: parseInt(props.startTimeInSeconds, 10) || 0
};
}
tick() {
this.setState(state => ({
seconds: state.seconds + 1
}));
}
componentDidMount() {
this.interval = setInterval(() => this.tick(), 1000);
}
componentWillUnmount() {
clearInterval(this.interval);
}
formatTime(secs) {
let hours = Math.floor(secs / 3600);
let minutes = Math.floor(secs / 60) % 60;
let seconds = secs % 60;
return [hours, minutes, seconds]
.map(v => ('' + v).padStart(2, '0'))
.filter((v,i) => v !== '00' || i > 0)
.join(':');
}
render() {
return (
<div>
Timer: {this.formatTime(this.state.seconds)}
</div>
);
}
}
ReactDOM.render(
<Timer startTimeInSeconds="300" />,
document.getElementById('timer-example')
);
In the component's componentDidMount lifecycle method, you can set an interval to call a function which updates the state.
componentDidMount() {
setInterval(() => this.setState({ time: Date.now()}), 1000)
}
class ShowDateTime extends React.Component {
constructor() {
super();
this.state = {
curTime : null
}
}
componentDidMount() {
setInterval( () => {
this.setState({
curTime : new Date().toLocaleString()
})
},1000)
}
render() {
return(
<div>
<h2>{this.state.curTime}</h2>
</div>
);
}
}
i myself like setTimeout more that setInterval but didn't find a solution in class based component .you could use sth like this in class based components:
class based component and setInterval:
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {
date: new Date()
};
}
componentDidMount() {
this.timerID = setInterval(
() => this.tick(),
1000
);
}
componentWillUnmount() {
clearInterval(this.timerID);
}
tick() {
this.setState({
date: new Date()
});
}
render() {
return (
this.state.date.toLocaleTimeString()
);
}
}
ReactDOM.render(
<Clock / > ,
document.getElementById('app')
);
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script>
<div id="app" />
function based component and setInterval:
https://codesandbox.io/s/sweet-diffie-wsu1t?file=/src/index.js
function based component and setTimeout:
https://codesandbox.io/s/youthful-lovelace-xw46p
So you were on the right track. Inside your componentDidMount() you could have finished the job by implementing setInterval() to trigger the change, but remember the way to update a components state is via setState(), so inside your componentDidMount() you could have done this:
componentDidMount() {
setInterval(() => {
this.setState({time: Date.now()})
}, 1000)
}
Also, you use Date.now() which works, with the componentDidMount() implementation I offered above, but you will get a long set of nasty numbers updating that is not human readable, but it is technically the time updating every second in milliseconds since January 1, 1970, but we want to make this time readable to how we humans read time, so in addition to learning and implementing setInterval you want to learn about new Date() and toLocaleTimeString() and you would implement it like so:
class TimeComponent extends Component {
state = { time: new Date().toLocaleTimeString() };
}
componentDidMount() {
setInterval(() => {
this.setState({ time: new Date().toLocaleTimeString() })
}, 1000)
}
Notice I also removed the constructor() function, you do not necessarily need it, my refactor is 100% equivalent to initializing site with the constructor() function.
Owing to changes in React V16 where componentWillReceiveProps() has been deprecated, this is the methodology that I use for updating a component.
Notice that the below example is in Typescript and uses the static getDerivedStateFromProps method to get the initial state and updated state whenever the Props are updated.
class SomeClass extends React.Component<Props, State> {
static getDerivedStateFromProps(nextProps: Readonly<Props>): Partial<State> | null {
return {
time: nextProps.time
};
}
timerInterval: any;
componentDidMount() {
this.timerInterval = setInterval(this.tick.bind(this), 1000);
}
tick() {
this.setState({ time: this.props.time });
}
componentWillUnmount() {
clearInterval(this.timerInterval);
}
render() {
return <div>{this.state.time}</div>;
}
}
This can be implemented even with the setTimeout instead of setInterval. As the useState re-renders the component, it will call the setTimeout again and again.
Here is my sample component which update the timer in every second. Also, Please let me know if I am making any mistake here.
import React, { useEffect, useState } from 'react'
export default function Footer() {
const [seconds, setSeconds] = useState((new Date()).getSeconds());
function GetTime() {
setSeconds((new Date()).getSeconds());
console.count(seconds);
}
setTimeout(() => {
console.log("Footer Rendered");
GetTime();
}, 1000);
return (
<footer>
<h2>Test Footer</h2>
<p>Copyright © {seconds}</p>
</footer>
)
}

Categories