Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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

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.

like image 454
AtoM_84 Avatar asked Jan 05 '16 21:01

AtoM_84


People also ask

What is Moment () in react?

For this, we'll be using a library called moment js. A lightweight JavaScript date library for parsing, validating, manipulating, and formatting dates.

Why We Use moment in react?

js as a better way to calculate date and time. If you are working in React Native or any other JavaScript framework, then you must have once come across using Time or Date in a formatted manner as per the requirements.

How do you use moments?

moment(). format('YYYY-MM-DD'); Calling moment() gives us the current date and time, while format() converts it to the specified format. This example formats a date as a four-digit year, followed by a hyphen, followed by a two-digit month, another hyphen, and a two-digit day.


2 Answers

@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>
    );
  }

}
like image 165
Brandon Avatar answered Oct 06 '22 01:10

Brandon


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.

like image 33
Krzysztof Sztompka Avatar answered Oct 06 '22 00:10

Krzysztof Sztompka