3

How properly refresh moment.from(). I tried set setInterval(this.renderReminders(), 1000) in componentDidMount, but doesn't work, popup error. How solve this issue?

class App extends Component {

renderReminders() {
    const { reminders } = this.props;
    return (
      <ListGroup>
        {
          reminders.map(reminder => {
            return (
              <ListGroupItem key={reminder.id}>              
                <div>{moment(reminder.dueDate, 'DD-MM-YYYY HH:mm').fromNow()}</div>             
              </ListGroupItem>
            ) // return
          }) // reminders.map
        }
      </ListGroup>
    ) // return
  } // renderReminders()
  
  render() {
    return (
      <div className="container">      
        { this.renderReminders() }
      </div>
    ) // return
  } // render
  
 } // App
Rami Chasygov
  • 2,714
  • 7
  • 25
  • 37

6 Answers6

8

I know I am late to post this but it could help others who are new to this problem.

IT IS BETTER TO USE react-moment with ease

REQUIREMENTS; moment, react-moment

import React from 'react'
import Moment from 'react-moment'

const UpdatesTheTime = () => {
    return (
        <Moment interval={1000} fromNow>
            {reminder.dueDate}
        </Moment>
    )
}

export default UpdatesTheTime

it will update every second as I set the interval to 1000

to see result watch it changes after 1 minute mark

I USE TIMESTAMP like this 1590115433736 for the time value and it works well

see https://openbase.io/js/react-moment for further info

there is parse or format that you can use for your time value.

Example < Moment parse="YYYY-MM-DD HH:mm" />

Aljohn Yamaro
  • 2,629
  • 25
  • 22
4

I'd say there's easily a couple ways to make timers in React.
One suggestion is to extrapolate Reminder into another react component.

Set the reminderTime in the new component's state and then create a method that usesthis.setState to update the timer.

setState will automatically re-render your component if the state changes.

In componentWillMount you should use setInterval to call a function that wil update the state. In componentWillUnmount you should get rid of the interval with clearInterval.

I didn't test the below code so it's mostly just to get the idea.

class Reminder extends React.Component{
  constructor(props){
      super(props);
      this.state = {
          reminderTime :  moment(this.props.dueDate, 'DD-MM-YYYY HH:mm').fromNow()},
      }
  }

  componentDidMount(){
      // add the interval
      this.interval = setInterval(this.updateReminderTime.bind(this), 3000);
  }

  componentWillUnmount(){
      // remove the interval listener
      clearInterval(this.interval);
  }

  updateReminderTime(){
     this.setState({
        reminderTime : moment(this.props.dueDate, 'DD-MM-YYYY HH:mm').fromNow()}
  }
  
  render(){
      return <div>{this.state.reminderTime }</div>
  }
}

class App extends Component {
renderReminders() {
    return (
      const reminders = this.props.reminders;
      <ListGroup>
        {
          reminders.map(reminder => {
            return (
              <ListGroupItem key={reminder.id}>              
                <Reminder dueDate={reminder.dueDate} />            
              </ListGroupItem>
            ) // return
          }) // reminders.map
        }
      </ListGroup>
    ) // return
  } // renderReminders()
  
  render() {
    return (
      <div className="container">      
        { this.renderReminders() }
      </div>
    ) // return
  } // render
  
 } // App
Khauri
  • 3,753
  • 1
  • 11
  • 19
  • may i ask? why u use ... bind(this) ... i hundred times read about that (something with context) although i cannot properly understand it – Rami Chasygov Jul 02 '17 at 13:55
  • @RamzanChasygov, If you want to attach a certain context(where your data is present) with your function call you can use bind(there are also call() and apply() too). When it comes to React especially in a class component, function definition and call can be at different places(i.e. defined in the class and calling from render) binding becomes important. There is an official doc for same https://reactjs.org/docs/faq-functions.html#why-is-binding-necessary-at-all – Pramod Mali Aug 12 '20 at 06:45
2

Instead of using one component to render multiple timers, I think you should create one component to render 1 timer, and then re-use it for all your cases(pass the timer as prop to it).

Here is a basic example of one.

class App extends React.Component {
  constructor(props) {
    super(props);
    this.state = {date: moment().format('DD-MM-YYYY HH:mm').fromNow()};
  }

  componentDidMount() {
    this.timerID = setInterval(
      () => this.tick(),
      1000
    );
  }

  componentWillUnmount() {
    clearInterval(this.timerID);
  }

  tick() {
    this.setState({
      date: moment().format('DD-MM-YYYY HH:mm').fromNow()
    });
  }

  render() {
    return (
      <div>
        date={this.state.date}
      </div>
    );
  }
}

Now you only need to pass the initial date as prop, and re-use this component for all your timers.

Sotiris Kiritsis
  • 3,178
  • 3
  • 23
  • 31
1

const {ListGroup, ListGroupItem} = Reactstrap;

class App extends React.Component {
  constructor(props) {
    super(props);
    
    this.state = {
      interval: null,
      reminders: this.nextReminders(props.reminders),
    };
    
    this.refreshReminders = this.refreshReminders.bind(this);
    this.nextReminders = this.nextReminders.bind(this);
  }
  
  componentDidMount() {
    const interval = setInterval(this.refreshReminders, 1000);
    this.setState({ interval });
  }
 
  componentWillUnmount() {
    const { interval } = this.state;
    clearInterval(interval);
  }
  
  refreshReminders() {
    const { reminders } = this.props;
    const nextReminders = this.nextReminders(reminders);
    
    this.setState({ reminders: nextReminders });
    
    console.log('refresh !');
  }
  
  nextReminders(reminders) {
    return reminders.map(reminder => {
      return {
        ...reminder,
        render: moment(reminder.dueDate, 'DD-MM-YYYY HH:mm').fromNow()
      };
    });
  }
  
  renderReminders() {
    const { reminders } = this.state;
    
    return (
      <ListGroup>
        {
          reminders.map(reminder => {
            return (
              <ListGroupItem key={reminder.id}>              
                <div>{reminder.render}</div>             
              </ListGroupItem>
            ) // return
          }) // reminders.map
        }
      </ListGroup>
    ) // return
  } // renderReminders()
  
  render() {
    return (
      <div className="container">      
        { this.renderReminders() }
      </div>
    ) // return
  } // render
  
 } // App
 
 const data = [
   {id: 1, dueDate: '02-07-2017 15:34'},
   {id: 2, dueDate: '02-07-2017 13:00'},
   {id: 3, dueDate: '02-07-2017 14:00'},
 ];
 
 ReactDOM.render(<App reminders={data} />, document.getElementById('app'));
<script src="https://cdnjs.cloudflare.com/ajax/libs/moment.js/2.18.1/moment.min.js"></script>
<link href="https://maxcdn.bootstrapcdn.com/bootstrap/4.0.0-alpha.6/css/bootstrap.min.css" rel="stylesheet"/>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/15.1.0/react-dom.min.js"></script>
<script src="https://unpkg.com/reactstrap/dist/reactstrap.min.js"></script>

<div id="app"></div>
soywod
  • 4,377
  • 3
  • 26
  • 47
1

Here is a component I made using hooks.

import moment from "moment";
import React, { useEffect, useState } from "react";

const formatter = (timestamp) => {
  return moment.unix(timestamp).fromNow();
};

export default ({ timestamp, interval }) => {
  const [timestampString, setTimestampString] = useState("");

  useEffect(() => {
    const timer = setInterval(
      () => setTimestampString(formatter(timestamp)),
      interval
    );
    setTimestampString(formatter(timestamp));
    return () => clearInterval(timer);
  }, []);
  return timestampString;
};

It can be used as:

<ReactMoment timestamp={1597213985} interval={1000} />

It updates the moment time every 1 second.

0

You should keep time remaining in state, and use setInterval to periodically update (decrement) that part of the state. This state update will then in turn trigger render of your component.

Davorin Ruševljan
  • 4,353
  • 21
  • 27