Little question about Reactivity with React and componentDidMount


#1

Hello Guys, im in troubles again : )

I developed a little clock / countdown timmer, on the server. Every second goes into the database, and i get the seconds from the client.

export default createContainer(() => {
	return {
		Countdown: Countdown.find({}, {
			limit: 1,
			sort: { lastUpdated: -1 }
		}).fetch()
	}
}, App);

My question is, ** in “App.js” render() {} with a simple map**

{this.props.Countdown.map((item) => {
// …
})}

i can get every second in real time from the server.

How can i do that on componentDidMount? Without using Meteor.setInterval to get every second from the database?

Cause i notice it gives me really bad results using setInterval to get every second.

Sorry for my english, hope u guys understand what im trying to say.

I will like to know if there is a way to fire the database from componentDidMount in real time, without using a setInterval or setTimeout.

Thanks!


#2

Unless the component in question is your container component I typically try to avoid calls and have everything passed in via props.

If the component your talking about is your container you can embed react in Blaze or use createContainer etc to provide reactive data access.


#3

First of all thanks for ur time and ur reply! And second thing, then… there isnt any way to have the data reactive like i have it in the App.js render() {} component?

I mean, i will like to know if i can fire the data in the componentDidMount from the from the collection without using a setinterval / setimeout, to get in this case every second from the countdown.

Well anyways thanks for ur reply, have a good day!


#4

You’re making things a lot harder than they need to be. You don’t need to keep sending data to all clients every second, only once. Even if you did, your clock wouldn’t tick every second due to latency.

Here’s a countdown component:

import React from 'react';
import { Meteor } from 'meteor/meteor';

export class Clock extends React.Component {
  constructor(props) {
    super(props);
    this.state = {
      targetDate: undefined,
      timeRemaining: undefined
    }
    Meteor.call('getTargetDate', (err, targetDate) => {
      this.setState({
        targetDate
      })
    });
    this.interval = setInterval(() => {
      const remaining = this.state.targetDate.getTime() - Date.now();
      const hours = Math.floor(remaining / 1000 / 60 / 60);
      const minutes = Math.floor(remaining / 1000 / 60) - (hours * 60);
      const seconds = Math.floor(remaining / 1000) - (minutes * 60) - (hours * 60 * 60);
      
      this.setState({
        timeRemaining: `${hours} Hours ${minutes} Minutes ${seconds} Seconds`
      })
    }, 500); // Clients will be off by ~half a sec.
  }
  componentWillUnmount() {
    clearInterval(this.interval);
  }
  render() {
    return (
      <div>
        {
          this.state.timeRemaining && this.state.timeRemaining.toString() || "Starting the countdown..."
        }
      </div>
    );
  }
}

And on /server/methods.js:

import { Meteor } from 'meteor/meteor';

const targetDate = new Date();
targetDate.setDate(targetDate.getDate() + 1);
Meteor.methods({
  getTargetDate() {
    return targetDate;
  }
});

Being off by half a second is more than acceptable in most situations because your users aren’t likely to open two windows side by side and notice the numbers being “slightly” off. You can use a smaller delay for setInterval to increase the accuracy of your clock though.


#5

Hola manuel, muchas gracias!

Thanks again manuel, i really apreciate the time u spend trying to help me a bit. I will check ur post slowly step by step, and figure it out all the things.

Thanks you so much! :slight_smile:


#6

Just a little modification to handle client disconnections and errors.
From the React Doc it says this about componentDidMount():

If you want to integrate with other JavaScript frameworks, set timers using setTimeout or setInterval, or send AJAX requests, perform those operations in this method.

getServerTime() {
 Meteor.call('getTargetDate', (err, targetDate) => {
if (!err) {
      this.setState({
        targetDate
      })
    } else {
             setTimeout(() => {
                 getServerTime();
            }, 1000); // Set as appropriate
    });
}
componentDidMount() {
 getServerTime();
}