Calculating Time in React
Hi there and welcome back to the react course, in the last guide we set up our clock component and we have our birthday showing up our birth date showing up when we just hit generate component 09/15/1987 generate shows the birth date we entered and then we have it displaying the amount left without the amount just the days hours minutes and seconds.
Guide Tasks
  • Read Tutorial
  • Watch Guide Video
Video locked
This video is viewable to users with a Bottega Bootcamp license

So let's go ahead and this guide we're going to make it so it actually displays the days, hours, minutes, and seconds. So in our completed app, It's going to work like this 09/15/1997 generate countdown 175 days and so on. So this guide we're going to be doing that. So the first thing I do is close this and back in our code let's go ahead and write out a function that is called getTimeRemaining and let's go ahead and bind this constructor this.state = {timeRemaining}and then

this.getTimeRemaining = this.getTimeRemaining.bind(this);

large

and basically, we need a way to access days, hours, minutes, and seconds. So the way we can do this is by returning an object that has these properties

return{
     'days': days,
     'hours': hours,
     'minutes': minutes,
     'seconds': seconds,

large

So now we have that setup let's go ahead and make sure these have values we're not displaying just something random so

var days = 0
var hours = 0
var minutes = 0
var seconds = 0

large

and now let's go ahead and let's set data equal to we could set it equal to a call to this so we can say this.getTimeRemaining and that would work it would get our object let's see in the browser if that works and it does. Days, hours, minutes, and seconds are all set to zero but we don't want to do it that way because when our form reloads we just want it to access the time. We want it to access our state we don't want to be calling this function. So let's go ahead and said this back to this.state.timeRemaining and then let's find a way we can modify our time remaining so that when the component lifecycle is going through and it selects render. When it runs render it will take our data and put it into our app into our DOM. All right so let's go ahead and let's do it in the componentDidMount or willMount because that's called before render. If we were to call componentDidMount it would render it and then it would render it again. So we'd be having 2 renderers and that's not efficient and we can actually try it out in our app by in the didMount typing in this.setState({ timeRemaining: this.getTimeRemaining(this.birthday) }). So the reason we're passing in this.birthday is that we're going to access these moments well I will show you that after. So basically we're going to call this.getTimeRemaining.

large

And let's also console.log('RENDERING'); so I can show you what's happening save that go to the app open up console and reload the app and then hit generate countdown you'll see it's rendering twice two times you reload that again and hit generate you'll see a go one-two really quick. So that's because we're hitting render and then we're calling componentDidMount and in our componentDidMountwe are setting state which is causing it to render again because componentDidMount triggers our re-rendering. And you can even see that here if you hold the command and hover over componentDidMount it says called immediately after a component is mounted, setting state here will trigger a re-render. We shouldn't do that because that's terribly inefficient. Let's go ahead and let's plop this into a different method called componentWillMount

large

and according to our guide here, it says componentWillMount comes right before render so this seems like an appropriate place to put it and it will work. So go ahead and reload this, generate and it's rendering once. But we don't need to put it into the componentWillMount because of our constructor and that's where we should set everything up. That's where we have access to our props directly so let's go ahead and do that. He might even say in here that I think he says in here that we shouldn't put it in the componentWillMountit says you should avoid calling any functions that cause side effects and that method sets date won't trigger a change and there is no DOM to interact with.

large

So I think he's wrong in that it says it won't. Okay, nevermind this componentWillMount I was thinking of didMount. So yeah it won't trigger a change since there's no DOM to interact with. So there's really no reason to be using this method. We should just put this into our constructor so let's go ahead and put this right below right here.

large

Save that go back to our app reload it generate countdown and it says

large

The reason we were getting that error is that we're trying to set state in the constructor before we even have state. So we could just set this equal to get timeRemaining and get rid of that completely.

large

And this will get our data, reload that generate countdown and we have our data in there nicely. Okay so now let's write the logic to calculate when our birthday actually is and how many days are left until it so in our getTimeRemaining let's start by grabbing our birthday and converting it to a date object since our birthday is not a date object. We also want to pass it into here as a parameter. We could just access it using this but it's considered better to put it in as a function that makes the function pure if you've ever heard of pure functions. So let's pass in birthday and let's make a var bday = new Date(birthday).

large

This will convert our moment into a birthday object or a date object. And then let's get a birthday or a date object called today and said that equal to new date that's going to give us the current date. In this case March 22nd I believe March 23 and those are our two dates so all we need to do now is minus the two so we can get the distance.

var bday = new Date(birthday);
let today = new Date();

var distance = bday.getTime() - today.getTime();

Okay so that will give us our distance and you might think how do we access the days, hours, and minutes and that's where we have to do a little bit of calculation. So if you alert distance we're going to get a big number and you'll see that when you reload the app, generate and it says not a number. It's because birthday is not set to anything it's null. We need to make sure that we're passing something into this function. So let's pass in this.props.birthdayFormState.startDate.toString.

large

It's a mouthful and it's exactly what we are putting in this componentDidMount method and we don't want to re-render the app so we're just going to delete with that for now.

large

So our state is equal to this.getTimeRemaining(this.props.birthdayFormState.startDate.toString. That's going to make our moment a string and then we're going to convert it to a date with that string because the date object takes in that format to create a date object. Okay, now when we alert that it will give us the distance between the two so that looks like it was just seconds let's go ahead and put a date like 2010 same year and day then that generate countdown and we are given this big long number that doesn't really mean much to us.

So the way we can format this is by doing a little Google search to find it out and typing in how to create a timer JavaScript and look the first one How to Create a timer and Javascript. Googling is important and you'll see this the exact thing we're looking for back in the design. You'll see it's the exact thing we're looking for right here in the wireframe we're seeing the same thing. So go to that and look at the code a little bit and it looks like this is the calculations required for days, hours, minutes, and seconds. So we have try it yourself and hit run. You'll see it's getting these all and then it's putting it into this paragraph tag. So all we need is to copy this.

    var days = Math.floor(distance / (1000 * 60 * 60 * 24));
    var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
    var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
    var seconds = Math.floor((distance % (1000 * 60)) / 1000);

Put this over into our code and now we have access to our days, hours, minutes, and seconds we can go ahead and delete this alert and these other variables save it reload our application and we should have access to these variables.

large

Let's go ahead and put in a date that is not the same it was put in a future date though so 2018, March 30th, six days three hours 29 minutes and six seconds. If we go to our completed app and put in the same thing which I believe is March 30th go ahead and hit generate we're given the same exact data. So that's how we generate the countdown the time between our birthday and today and that's all we will be doing in this guide. In the next guide, we're going to learn how we can set this up with a JavaScript timer using JavaScript timing events to make it actually countdown. You'll notice that nothing is counting down. So we're going to learn how we can make it actually countdown like this application cations doing right here. So let's go ahead and commit our code really quick

git status
git add .
git commit -m 'added time calculation'
git push origin master

All right and I'll see you in the next guide.

Resources

Source Code