Creating a React Calendar Component: Part 1
Tags: react, javascript, calendar, webdev
Published: 2020-07-10 02:04:00

In a recent discussion with a colleague of mine, we touched upon the subject of projects for our portfolios. The conversation eventually touched upon the idea that you don’t need to create huge functioning programs to showcase your knowledge. Mini-projects are more than enough to showcase a specific skill-set, something I kept in mind as I worked on my next item.

In my everyday work, I am primarily a back-end developer working with NodeJs and serverless frameworks. Every once in a while, I’ll work on front-end code, but always find myself struggling to grasp the latest changes that my colleagues put out into our product. With this in mind, I set about creating my own components while brushing up on my knowledge with React. So in this 4 part series, I will show how I went about creating a calendar component in React.

Please note, that I will not be going deep into the setup of the project and going straight into the process. If you want more knowledge or an easy way to start your own React App, check out [Create React App](https://reactjs.org/docs/create-a-new-react-app.html).

![Calendar example](https://dev-to-uploads.s3.amazonaws.com/i/mcu4i60vwou4nml5idv1.png)
Created using React, Javascript, and MomentJs

In this post (part 1), we will strictly look at the logic that went into generating the days of the month and the overflow dates of the previous and following month. Having come across little “gotcha” moments, I hope that this post will provide some knowledge and an interesting read for you if you choose to create your own components in the future.

At a quick glance, a calendar seems simple and straightforward. Let’s grab a bunch of dates and slap it on a screen! Of course, things aren’t as simple as we want them to be and a few things had to be considered before writing any code:

1.  _How do I get the number of days in a given month?_
2.  _How do I know the first day of the week for a given month?_
3.  _How do I get the overflow dates of the previous/following months?_

Before moving forward, I encourage you to take a moment and think about how _you_ would go about this. After all, mental exercise keeps you fit!

**Challenge 1: How do I get the number of days in a given month?**

Dates are a tricky thing in programming and the Javascript and NodeJs community have a special place in their hearts for MomentJs. This challenge is simple with the amazing functionality this library provides which we will be taking advantage of by installing the package in our project root with `npm install --save moment` .

MomentJs has the function `daysInMonth()` . Problem solved! Let’s see what it gives when you give it a month.

```javascript
import moment from 'moment';

const getDaysInMonth = (month) => {  
  return moment(month, 'MM').daysInMonth();  
}

// '01' = January...  
console.log(getDaysInMonth('01'))
```

The console should spit back 31 as the result. Simple right? This works, but this has a few challenges. Moment by default assumes any missing information by getting it from the current date which means that this is getting the days of the month of January 2020 even though we don’t pass in the year directly. Of course 2020 is a unique year… which you guessed, a leap year!

This function would work if I passed in `getDaysInMonth('02') // 29` , but what if I want the days of February in 2019? Thankfully, MomentJs has functionality to handle that for us.

```javascript
import moment from 'moment';

const getDaysInMonth = (month, year) => {  
  return moment(\`${month}-${year}\`, 'MM-YYYY').daysInMonth();  
}

console.log(getDaysInMonth(2, 2019))
```

Ta da! Challenge solved. The function can now accept the month and the year as its arguments and correctly determine the number of days in the month _of a specific year._

**Challenge 2: How do I know the first day of the week for a given month?**

This challenge is a relatively simple one, so I won’t spend too much time on this. However, let’s take some time to figure out why we want this information. If you’ve seen most date objects in Javascript, you’ll see something like this:

```javascript
new Date() // 2020-07-07T05:00:00:000Z
```

There are two ways to do this. First, lets take a look at the vanilla Javascript way:

```javascript
const getFirstWeekdayOfMonth = (month, year) => {  
  return new Date(year, month, 1).getDay()  
}

// Note, the month and result is 0-indexed  
console.log(getFirstWeekdayOfMonth(0, 2020))
```

The month is represented as `0` which equals January so the first weekday of January in 2020 was Wednesday which is indexed at `3`. If you’re confused about the indexing, `[0, 1, 2, 3, 4, 5, 6] = Sunday — Saturday`. The solution is simple and for the most part will work, but working with dates is a pain and little caveats here and there cause some challenges. Still, it is good to know so if you are interested, learn more about [Javascript Dates](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date).

Now, let’s take advantage of MomentJs to do the same thing!

```javascript
const getFirstWeekdayOfMonth = (month, year) => {  
  return moment(  
    \`${month}-${year}\`,   
    'MM-YYYY'  
  ).startOf('month').weekday()  
}

// Note, since we use MomentJs's formatting, we do not index the month. This is getting the first weekday of the month for January 2020. Result is 0-indexed  
console.log(getFirstWeekdayOfMonth(1, 2020))
```

Simple, result is the same with `3!`

**Challenge 3: How do I get the overflow dates of the previous/following months?**

![Alt Text](https://dev-to-uploads.s3.amazonaws.com/i/mcu4i60vwou4nml5idv1.png)

The last challenge is to figure out exactly how many days we need to show in the overflow of the previous and following months. Looking at this picture of the finished component, we want the information regarding the dates that are greyed out.

But before we begin, let’s do some quick logic.

We know that there can be anywhere from 28–31 days in a given month. In one week, there are 7 days. Assuming that the first day of the month can be on any given weekday, we want to know how many weeks a given month can be a part of. Taking a look at the picture above, we know that July 2020 lands on 5 weeks. But, wait… what if the first day of the month is Saturday?

If the first day of the month is Saturday, a month can be a part of 6 weeks. Given that 31 days is the maximum number of days in a month, 6 weeks is the most any given month can be a part of. This is considering no new date conventions arise in our lifetimes. I hope that I won’t be around when developers need to start working with dates across multiple planets!

Since we know that 6 weeks is the maximum number of weeks any given month can be a part of, let’s say that our calendar needs to show a total of 42 dates (7 \* 6 = 42).

Now, let’s figure out the previous month’s overflow dates. To do so, we need to know the current month and first weekday to display using the functions we created above. First, let’s add the first month into an array after constructing the date using MomentJs.

```javascript
const getDatesInMonthDisplay = (month, year) => {  
  const daysInMonth = getDaysInMonth(month, year);  
  const firstWeekday = getFirstWeekdayOfMonth(month, year);  
  const result = \[\];

  for (let i = 1; i <= daysInMonth; i++) {  
    result.push(  
      moment(\`${month}-${i}-${year}\`, 'MM-DD-YYYY').toDate()  
    )  
  }

  return result;  
}

// July 2020

// Note, since we use MomentJs's formatting, we do not index the month. This is getting the first weekday of the month for January 2020. Result is 0-indexed

console.log(getDatesInMonthDisplay(7, 2020))
```

The result should consist of an array of date objects representing each day of the month of July 2020.

```javascript
[ 
  2020-07-01T07:00:00.000Z,  
  2020-07-02T07:00:00.000Z,  
  2020-07-03T07:00:00.000Z,  
  2020-07-04T07:00:00.000Z,  
  2020-07-05T07:00:00.000Z,  
  ...  
]
```

Taking a look at the `firstWeekday` variable, we can determine how many days of the previous month we need in our overflow. For July 2020, the first weekday, as we determined above, is Wednesday or an index number of 3. Therefore, we know that we need 3 days of the previous month to complete a full week at the start of the month display… \[0, 1, 2…\].

First, let’s add two quick helper functions to determine the previous/following month and years!

```javascript
const getPrevMonthYear = (month, year) => {  
  // If it is January... prev month is Dec of the previous year  
  if (month === 1) {   
    return {  
      month: 12,  
      year: year - 1  
    }  
  }

  // Otherwise, same year, but month - 1  
  return {  
    month: month - 1,  
    year  
  }  
}

const getNextMonthYear = (month, year) => {  
  // If it is January... prev month is Dec of the previous year  
  if (month === 1) {   
    return {  
      month: month + 1,  
      year  
    }  
  }

// Otherwise, same year, but month - 1  
  return {  
    month: 12,  
    year: year + 1  
  }  
}
```

Now, using the helper function…

```javascript
const getDatesInMonthDisplay = (month, year) => {  
  const daysInMonth = getDaysInMonth(month, year);  
  const firstWeekday = getFirstWeekdayOfMonth(month, year);  
  const result = \[\];

  const prev = getPrevMonthYear(month, year);  
  const prevDaysInMonth = getDaysInMonth(  
    prev.month,   
    prev.year  
  );

  // Add prev overflow dates...   
  for (let j = firstWeekday - 1; j >= 0; j--) {  
    result.push(  
      moment(  
        \`${prev.month}-${prevDaysInMonth - j}-${prev.year}\`,   
        'MM-DD-YYYY'  
      ).toDate()  
    )  
  }

  // Add current month's dates  
  for (let i = 1; i <= daysInMonth; i++) {  
    result.push(  
      moment(\`${month}-${i}-${year}\`, 'MM-DD-YYYY').toDate()  
    )  
  }

  return result;  
}

// July 2020

// Note, since we use MomentJs's formatting, we do not index the month. This is getting the first weekday of the month for January 2020. Result is 0-indexed

console.log(getDatesInMonthDisplay(7, 2020))
```

Now we should have the array correctly start with the previous month’s days leading up to the first weekday of the current active month on display. Let’s fill up the rest with the next month’s overflow dates!

Using the helper `getNextMonthYear` …

```javascript
const getDatesInMonthDisplay = (month, year) => {  
  const daysInMonth = getDaysInMonth(month, year);  
  const firstWeekday = getFirstWeekdayOfMonth(month, year);  
  const result = \[\];

const prev = getPrevMonthYear(month, year);  
  const prevDaysInMonth = getDaysInMonth(  
    prev.month,   
    prev.year  
  );

  // Add prev overflow dates...   
  for (let j = firstWeekday - 1; j >= 0; j--) {  
    result.push(  
      moment(  
        \`${prev.month}-${prevDaysInMonth - j}-${prev.year}\`,   
        'MM-DD-YYYY'  
      ).toDate()  
    )  
  }

  // Add current month's dates  
  for (let i = 1; i <= daysInMonth; i++) {  
    result.push(  
      moment(\`${month}-${i}-${year}\`, 'MM-DD-YYYY').toDate()  
    )  
  }

  // Overflow dates for next month to meet 42 days per month display   requirement  
  if (result.length < 42) {  
    const daysToAdd = 42 - result.length;  
    const next = getNextMonthYear(month, year);

    for (let k = 1; k <= daysToAdd; k++) {  
      result.push(  
        moment(  
          \`${next.month}-${k}-${next.year}\`,   
          'MM-DD-YYYY'  
        ).toDate()  
      )  
    }  
  }

  return result;  
}

// July 2020

// Note, since we use MomentJs's formatting, we do not index the month. This is getting the first weekday of the month for January 2020. Result is 0-indexed

console.log(getDatesInMonthDisplay(7, 2020))
```

And… viola! We have the total number of days in date objects to pass into the month display in our calendar component including the current month and the previous and following overflow dates! This `result.length` is `42` ensuring that the first index is a Sunday and the last index is a Saturday.

```javascript
[ 
  2020-06-28T07:00:00.000Z,  
  2020-06-29T07:00:00.000Z,  
  ...,  
  2020-08-07T07:00:00.000Z,  
  2020-08-08T07:00:00.000Z 
]
```

Before we wrap up, let’s add some quick information to make it easier to determine which dates are a part of the current month on display.

```javascript
const getDatesInMonthDisplay = (month, year) => {  
  const daysInMonth = getDaysInMonth(month, year);  
  const firstWeekday = getFirstWeekdayOfMonth(month, year);  
  const result = \[\];

const prev = getPrevMonthYear(month, year);  
  const prevDaysInMonth = getDaysInMonth(  
    prev.month,   
    prev.year  
  );

  // Add prev overflow dates...   
  for (let j = firstWeekday - 1; j >= 0; j--) {  
    result.push({  
      date: moment(  
        \`${prev.month}-${prevDaysInMonth - j}-${prev.year}\`,   
        'MM-DD-YYYY'  
      ).toDate(),  
      currentMonth: false  
    })  
  }

  // Add current month's dates  
  for (let i = 1; i <= daysInMonth; i++) {  
    result.push({  
      date:moment(\`${month}-${i}-${year}\`, 'MM-DD-YYYY').toDate(),  
      currentMonth: true  
    })  
  }

  // Overflow dates for next month to meet 42 days per month display   requirement  
  if (result.length < 42) {  
    const daysToAdd = 42 - result.length;  
    const next = getNextMonthYear(month, year);

for (let k = 1; k <= daysToAdd; k++) {  
      result.push({  
        date: moment(  
          \`${next.month}-${k}-${next.year}\`,   
          'MM-DD-YYYY'  
        ).toDate(),  
        currentMonth: false  
      })  
    }  
  }

  return result;  
}

// July 2020

// Note, since we use MomentJs's formatting, we do not index the month. This is getting the first weekday of the month for January 2020. Result is 0-indexed

console.log(getDatesInMonthDisplay(7, 2020))
```

Minor details like this make it easier when we pass it to the component and they help more than you’d think. Here’s an example of the new result.

```javascript
[ 
  { date: 2020-06-28T07:00:00.000Z, currentMonth: false },  
  { date: 2020-06-29T07:00:00.000Z, currentMonth: false },  
  { date: 2020-06-30T07:00:00.000Z, currentMonth: false },  
  { date: 2020-07-01T07:00:00.000Z, currentMonth: true },  
  ...,  
  { date: 2020-08-07T07:00:00.000Z, currentMonth: false },  
  { date: 2020-08-08T07:00:00.000Z, currentMonth: false } 
]
```

Next up in [part 2](https://dev.to/bertbae/creating-a-react-calendar-component-part-2-1nep), we will take a look at the rendering logic of React using this function to construct the dates to display for us.