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

六月ゝ 毕业季﹏ 提交于 2020-01-03 13:56:08

问题


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.


回答1:


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

}



回答2:


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.



来源:https://stackoverflow.com/questions/34621442/where-to-apply-my-moment-function-in-a-react-component

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!