Creating a React Calendar Component: Part 2
Tags: react, javascript, webdev, html
Published: 2020-07-10 02:24:00

Looking at a new framework in web development can be daunting. Technology moves quickly in this industry and learning new skills is a necessity. Thankfully, new skills don’t mean the we need to learn a new framework every time we start a new project. Diving deeper into an existing one or even revisiting it after some time to stay up to date with new changes are just as important.

In [part one](https://dev.to/bertbae/creating-a-react-calendar-component-part-1-31j1) of this series, we explored the logic behind creating the dates for us to display on our React calendar component. Now, we will dive into React itself and how the calendar component takes shape in the world of JSX using the function `getDatesInMonthDisplay` we created in part 1. If you are not familiar with how we created this function and want to know, check out [part one of the series](https://dev.to/bertbae/creating-a-react-calendar-component-part-1-31j1).

Before we begin, I will not be doing a deep dive into how React works with the assumption that you, my readers, understand the basic concepts of React’s rendering engine and how it is controlled by `state` and `props` . If you are not familiar with React, I recommend creating a simple project to understand the fundamental concepts with their [official get started page](https://reactjs.org/docs/getting-started.html). Otherwise, read on!

![Complete calendar component](https://dev-to-uploads.s3.amazonaws.com/i/weqz4yevzlmgvx4gqfgw.png)

Here is the completed React component and today, we will be pulling this apart into 4 parts.

1.  _Calendar header component_
2.  _Weekday indicator component_
3.  _Date indicator component_
4.  _Month indicator component_

Throughout each part, we will spend time on state management as we discuss the reasons behind why

**Section 1: Calendar header**

React is most commonly used in single page applications and if coded correctly, can be incredibly efficient in re-rendering parts of the web or application as data changes. This is done through something called `state` whereby the code looks for changes in data for defined states we want to track.

Since the calendar component wants to display one month at a time, let’s get it to show the month of the date the user selects.

```javascript
import React, { useState } from 'react';  
import moment from 'moment'  
import './bae-calendar.scss';

const BaeCalendar = () => {  
  const [selectDate, setSelectDate] = 
  useState(moment().toDate());

  return (  
    <div className="bae-calendar-container">  
      Hello World  
    </div>  
  );  
};

export default BaeCalendar;
```

Using React’s `useState` hook, we create a state called `selectDate` like this and set an initial state by using MomentJs to call `moment().toDate()` to get today’s date object (e.g. `2020–07–08T00:00:00.000Z` ).

```javascript
...  
const [selectDate, setSelectDate] = useState(moment().toDate());  
...
```

Now that we have a date object to work with, let’s take a look at our calendar header. The calendar, in my view, has 4 logical components and the header was the easiest place to start. Here is the full component and how the sub-component called `CalendarHeader` is pulled into the `BaeCalendar` component which will be the root file `index.js` of the folder.

```javascript
import React, { useState } from 'react';  
import moment from 'moment'  
import './bae-calendar.scss';

import CalendarHeader from './components/calendar-header';

const BaeCalendar = () => {  
  const [selectDate, setSelectDate] = 
  useState(moment().toDate());

  return (  
    <div className={`bae-calendar-container ${themes[theme]}`}>  
      <CalendarHeader selectDate={selectDate}/>  
    </div>  
  );  
};

export default BaeCalendar;
```

![Here is how the `CalendarHeader` looks on it’s own!](https://dev-to-uploads.s3.amazonaws.com/i/zqofjd4rh27h5dats3z5.png)
Here is how the `CalendarHeader` looks on it’s own!

Let’s take a look at the header component file that utilizes MomentJs to format the date object into what we need. Simple right? MomentJs’s formatting capabilities are top knotch and if you want to learn more, check out the documentation on what the `MMMM do` and `dddd` do in their [official documentation](https://momentjs.com/docs/#/displaying/format/).

```javascript
import React from 'react';  
import moment from 'moment'

const CalendarHeader = ({ selectDate }) => {  
  return (  
    <div className="bae-calendar-header">  
      <div className="left-container">  
        <h1>{moment(selectDate).format('dddd')}</h1>  
        <h1>{moment(selectDate).format('MMMM Do')}</h1>  
      </div>  
      <div className="right-container">  
        <h3>{moment(selectDate).year()}</h3>  
      </div>  
    </div>  
  );  
};

export default CalendarHeader;
```

You’ll also notice here that somehow, our `CalendarHeader` component has access to a `state` we created in the main `BaeCalendar` parent component. This is done by passing in what we call `props` . Here is how it looks in the main component as it passes in the props:

```javascript
<CalendarHeader selectDate={selectDate}/>
```

And accessed in the `CalendarHeader` component:

```javascript
const CalendarHeader = ({ selectDate }) => {  
  ...  
}
```

Now this component has access to this data! Props can be anything and does not have to be strictly `state` data, so get creative. If you’re still not sure how `props` work, check out React’s [official get started page](https://reactjs.org/docs/getting-started.html) and create a small project to play around.

Now.. this is a great start, but there’s something we can improve. We are going to be doing a lot of formatting throughout the calendar component and duplicate code is bad. So, let’s take a moment here and create a utility file called `moment-utils.js` which will handle the formatting for us. Below is all of the various formats we’ll end up using in our component and we will use this moving forward.

```javascript
import moment from 'moment';

export const getSpecificDate = (month, dayOfMonth, year) => {  
  return moment(`${month}-${dayOfMonth}-${year}`, 'MM-DD-YYYY').toDate();  
};

export const getDayOfMonth = (date) => moment(date).date();

export const getMonth = (date) => moment(date).month();

export const getYear = (date) => moment(date).year();

export const getToday = () => moment().toDate();

export const getReadableWeekday = (date) => moment(date).format('dddd');

export const getReadableMonthDate = (date) => moment(date).format('MMMM Do');

export const getMonthDayYear = (date) => moment(date).format('MM-DD-YYYY');
```

So our `CalendarHeader` will now look like this.

```javascript
import React from 'react';  
import {  
  getReadableMonthDate,  
  getReadableWeekday,  
  getYear,  
} from '../utils/moment-utils';

const CalendarHeader = ({ selectDate }) => {  
  return (  
    <div className="bae-calendar-header">  
      <div className="left-container">  
        <h1>{getReadableWeekday(selectDate)}</h1>  
        <h1>{getReadableMonthDate(selectDate)}</h1>  
      </div>  
      <div className="right-container">  
        <h3>{getYear(selectDate)}</h3>  
      </div>  
    </div>  
  );  
};

export default CalendarHeader;
```

**Section 2: Weekday indicator component**

Now the next section we’ll tackle is the weekday indicator showing the [Sunday — Saturday] representation in our component.

```javascript
import React, { useState } from 'react';  
import { getToday } from './utils/moment-utils';  
import './bae-calendar.scss';

import CalendarHeader from './components/calendar-header';  
import WeekdayIndicator from './components/weekday-indicator';

const BaeCalendar = () => {  
  const [selectDate, setSelectDate] = useState(moment().toDate());  
  return (  
    <div className={`bae-calendar-container ${themes[theme]}`}>  
      <CalendarHeader selectDate={selectDate}/>  
      <WeekdayIndicator />  
    </div>  
  );  
};  

export default BaeCalendar;
```

The `WeekdayIndicator` is quite simple. For all intents and purposes, we don’t actually need to pass any state or props to it. In fact, it’s responsibility is singular which is to display the days of the week.

```javascript
import React from 'react';

const WeekdayIndicator = () => {  
  return (  
    <div className="bae-weekday-indicators">  
      <div className="weekday-indicator-icon">  
        Sun  
      </div>  
      <div className="weekday-indicator-icon">  
        Mon  
      </div>  
      <div className="weekday-indicator-icon">  
        Tue  
      </div>  
      <div className="weekday-indicator-icon">  
        Wed  
      </div>  
      <div className="weekday-indicator-icon">  
        Thu  
      </div>  
      <div className="weekday-indicator-icon">  
        Fri  
      </div>  
      <div className="weekday-indicator-icon">  
        Sat  
      </div>  
    </div>;  
  )  
};

export default WeekdayIndicator;
```

Technically this works, but what a pain typing it out! Let’s re-do this in the “Ways of React”.

```javascript
import React from 'react';

const weekdays = ['Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat'];

const WeekdayIndicator = () => {  
  const weekdayIcons = weekdays.map((day, key) => {  
    return (  
      <div className="weekday-indicator-icon" key={key}>  
        {day}  
      </div>  
    );  
  });  
  return <div className="bae-weekday-indicators">{weekdayIcons}</div>;  
};

export default WeekdayIndicator;
```

First, by creating an array of the weekdays, we can utilise JavaScript's `.map` iterator method to create React JSX code. Since `.map` returns a new array, this new array assigned to the variable `weekdayIcons` which returns the following per iteration…

```javascript
<div className="weekday-indicator-icon" key={key}>  
  {day}  
</div>
```

You’ll notice a few things here. Why does every element have a `key` prop passed into it and what is `{...}` doing?

When creating multiple elements using a loop, React expects you to pass a `key` prop of unique values. Otherwise, it will complain with a warning which is always annoying to see in our web consoles. As for the curly braces, React automatically assumes any data put inside of it can be a variable. Of course, you can pass in a string value, but that defeats the purpose of its use.

This allows us to pass the `weekdayIcons`into the wrapping `div` element to have the same result as typing out each element individually like this.

```javascript
...  
return <div className="bae-weekday-indicators">{weekdayIcons}</div>  
...
```

![Here’s where we are at now!](https://dev-to-uploads.s3.amazonaws.com/i/y2lzykh7kppdt8paq3je.png)
Here’s where we are at now!

**Section 3: Date indicator**

Thankfully in the previous post ([calendar date display logic](https://dev.to/bertbae/creating-a-react-calendar-component-part-1-31j1)), we did the bulk of the work to create the date indicators.

```javascript
import React, { useState } from 'react';  
import { getToday } from './utils/moment-utils';  
import './bae-calendar.scss';

import CalendarHeader from './components/calendar-header';  
import WeekdayIndicator from './components/weekday-indicator';  
import DateIndicator from './components/date-indicator';

const BaeCalendar = () => {  
  const [selectDate, setSelectDate] = useState(moment().toDate());  
  return (  
    <div className={`bae-calendar-container ${themes[theme]}`}>  
      <CalendarHeader selectDate={selectDate}/>  
      <WeekdayIndicator />  
      <DateIndicator  
        selectDate={selectDate}  
        setSelectDate={setSelectDate}  
      />  
    </div>  
  );  
};  

export default BaeCalendar;
```

You’ll notice we’re passing in two `props` to the `DateIndicator` component, but for this part of the series, ignore the second one called `setSelectDate` and focus on how we will use`selectDate`. Let’s take a look!

```javascript
import React from 'react';  
import {  
  getDayOfMonth,  
  getMonthDayYear,  
  getMonth,  
  getYear,  
} from '../utils/moment-utils';  
import { getDatesInMonthDisplay } from '../utils/date-utils';

const DateIndicator = ({ selectDate, setSelectDate }) => {  
  const datesInMonth = getDatesInMonthDisplay(  
    getMonth(selectDate) + 1,  
    getYear(selectDate)  
  );

  const monthDates = datesInMonth.map((i, key) => {  
    return (  
      <div  
        className="date-icon"}  
        data-active-month={i.currentMonth}  
        data-date={i.date.toString()}  
        key={key}  
        onClick={changeDate}  
      >  
        {getDayOfMonth(i.date)}  
      </div>  
    );  
  });

return <div className="bae-date-indicator">{monthDates}</div>;  
};

export default DateIndicator;
```

By utilizing MomentJs and the helper functions `getMonth` and `getYear` , we can get an array of objects with properties `date` and `currentMonth` using the `selectDate` prop! So whichever date the `selectDate` represents, the `DateIndicator` is able to use `getDatesInMonthDisplay` to pull every single date in any month and year.

First, aren’t you glad that we already went through the logic of determining how many dates in the month we need for the display in the `getDatesInMonthDisplay` function before?

Similar to how we created each day of the week in the `WeekIndicator` component, we utilize the `.map` iterator here as well. Rightfully so, because if we had to type this out 42 times… well let me go get some beer first.

```javascript
const monthDates = datesInMonth.map((i, key) => {  
  return (  
    <div  
      className="date-icon"  
      data-active-month={i.currentMonth}  
      data-date={i.date.toString()}  
      key={key}  
    >  
      {getDayOfMonth(i.date)}  
    </div>  
 );  
});
```

Let’s break down how we are utilising each item in the array which represents an object with the properties `date` (date object) and `currentMonth` (boolean).

First, the `div` element has a inner content using `getDayOfMonth(i.date)` which is making use of `moment(_date_).date()` returning the numerical day of the month. If we didn’t do this and simply passed `i.date.toString()` (.toString() because we can’t pass a date object into HTML)… well, here’s the chaos that would have any UX/UI designer screaming at you.

![Ew…](https://dev-to-uploads.s3.amazonaws.com/i/75o0q3ng72927nlufrm1.png)
Ew…

However, this date object is incredibly useful even if it is not friendly to see on the UI of the component which is why we pass it into the data-attribute called `data-date` as a string. Here is how the element looks in the web console.

![Web console view](https://dev-to-uploads.s3.amazonaws.com/i/0i0g5da3l9tkcvfl1f09.png)

Simply by using vanilla Javascript, we could do something to have access to the date object of a specific element which we will utilise later on like this.

```javascript
document.querySelector('.selected[data-date]').getAttribute('data-date')  
// Fri Jul 10 2020 00:00:00 GMT-0700 (Pacific Daylight Time)
```

Finally, `data-active-month={i.currentMonth}` provides a `"true"` or `"false"` to the data attribute. Can you guess what it’s used for? If you are not sure, make sure you follow up for the third part of this series where I will discuss it further.

Given where we are now, we have enough to make our component interactive. As you can see in a few of the photos, there is a circle highlight that represents the selected date by a user. Let’s see how that works with the `useState` React hook called `setSelectDate` .

```javascript
import React from 'react';  
import {  
  getDayOfMonth,  
  getMonthDayYear,  
  getMonth,  
  getYear,  
} from '../utils/moment-utils';  
import { getDatesInMonthDisplay } from '../utils/date-utils';

const DateIndicator = ({ activeDates, selectDate, setSelectDate }) => {

  // EVENT HANDLING CALLBACK  
  const changeDate = (e) => {  
    setSelectDate(e.target.getAttribute('data-date'));  
  };

  const datesInMonth = getDatesInMonthDisplay(  
    getMonth(selectDate) + 1,  
    getYear(selectDate)  
  );

  const monthDates = datesInMonth.map((i, key) => {  
    const selected =  
      getMonthDayYear(selectDate) === getMonthDayYear(i.date) ? 'selected' : '';  
    const active =  
      activeDates && activeDates[getMonthDayYear(i.date)] ? 'active' : '';

    return (  
      <div  
        className={`date-icon ${selected} ${active}`}  
        data-active-month={i.currentMonth}  
        data-date={i.date.toString()}  
        key={key}

        // EVENT HANDLER  
        onClick={changeDate}  
      >  
        {getDayOfMonth(i.date)}  
      </div>  
    );  
  });

  return <div className="bae-date-indicator">{monthDates}</div>;  
};

export default DateIndicator;
```

Taking a look at the code above, find `setSelectDate` and you will notice that it is used inside of a function called `changeDate` . Javascript by nature is a browser language and event handling is its specialty. If you are not familiar with events in Javascript, read about it in [MDN](https://developer.mozilla.org/en-US/docs/Web/Events), it is the bread and butter of the browser language.

Following where `changeDate` is used, you’ll notice that each `date-icon` element has a `prop` called `onClick` that passes in the `changeDate` as a callback function. This means that when any of the `date-icon` elements are clicked, it will trigger the function setting off the `setSelectDate` . The value it passes as the argument to `setSelectDate` utilises what I showcased above using the data attribute `data-date` .

The code below responds to the click event which is represented by `e` . By accessing the target and the `data-date` attribute, we can grab the new date we want to select and change the `state` called `selectDate` .

```javascript
(e) => e.target.getAttribute('data-date')
```

By now, you can change the function `changeDate` to the following to see the new selected date be console logged into the web console, but since you have not yet applied any styling, you won’t see the changes in the icon. However, since the `state` is still changing, you should see the `CalendarHeader` component’s data update as it re-renders any components utilising the state `selectDate` !

```javascript
const changeDate = (e) => {  
  console.log(e.target.getAttribute('data-date');  
  setSelectDate(e.target.getAttribute('data-date'));  
}
```

**Almost there… Section 4: Month indicators**

By now, you should have a functioning calendar component that can change the `CalendarHeader` data with new selected dates and even change the month’s display by clicking one of the overflow dates. Let’s wrap up part 2 of this series by adding in the `MonthIndicator` component!

```javascript
import React, { useState } from 'react';  
import { getToday } from './utils/moment-utils';  
import './bae-calendar.scss';  
import CalendarHeader from './components/calendar-header';  
import WeekdayIndicator from './components/weekday-indicator';  
import DateIndicator from './components/date-indicator';  
import MonthIndicator from './components/month-indicator';

const BaeCalendar = () => {  
  const [selectDate, setSelectDate] = useState(moment().toDate());  
  return (  
    <div className={`bae-calendar-container ${themes[theme]}`}>  
      <CalendarHeader selectDate={selectDate}/>  
      <WeekdayIndicator />  
      <DateIndicator  
        selectDate={selectDate}  
        setSelectDate={setSelectDate}  
      />  
      <MonthIndicator   
        selectDate={selectDate}   
        setSelectDate={setSelectDate}  
      />  
    </div>  
  );  
};  

export default BaeCalendar;
```

Last sub-component to do, let’s get in there take a look at how it’s constructed.

```javascript
import React from 'react';  
import { getMonth } from '../utils/moment-utils';  
import { getMonthSet } from '../utils/date-utils';  
import './month-indicator.scss';

import { monthsFull } from '../constants/dates';

const MonthIndicator = ({ selectDate, setSelectDate }) => {  
  const changeMonth = (e) => {  
    setSelectDate(e.target.getAttribute('data-date'));  
  };

  const monthSet = getMonthSet(selectDate);

  return (  
    <div className="bae-month-indicator">  
      <h4 data-date={monthSet.prev} onClick={changeMonth}>  
        {monthsFull[getMonth(monthSet.prev)]}  
      </h4>  
      <h3>{monthsFull[getMonth(monthSet.current)]}</h3>  
      <h4 data-date={monthSet.next} onClick={changeMonth}>  
        {monthsFull[getMonth(monthSet.next)]}  
      </h4>  
    </div>  
  );  
};

export default MonthIndicator;
```

We see two `props` again here (`selectDate` and `setSelectDate` ). By now, it’s clear why we need `selectDate` . Using the current selected date, we can pull out the current, previous, and following month. Can you think of any challenges we might have determining the previous and following months based on the current one?

Two months immediately come to mind which are `December` and `January` . By design, we want these elements to be clickable to change the month on display. If we only took the current month and used moment to subtract or add a month, it obviously would not work for all cases. Going from `January` to `December` means that the year changes with the same logic applied in reverse.

So… let’s create a little helper function to handle this for us!

```javascript
const getMonthSet = (selectDate) => {  
  const month = getMonth(selectDate) + 1;  
  const result = {  
    current: selectDate,  
    prev: getSpecificDate(month - 1, 1, getYear(selectDate)),  
    next: getSpecificDate(month + 1, 1, getYear(selectDate)),  
  };

  if (month === 1) {  
    result.prev = getSpecificDate(12, 1, getYear(selectDate) - 1);  
  }

  if (month === 12) {  
    result.next = getSpecificDate(1, 1, getYear(selectDate) + 1);  
  }

  return result;  
};
```

Straightforward right? By getting the month of the currently selected date (+1 since months return in indexed form), we can use MomentJs to construct the `prev` and `next` month’s date objects. If the month is `1` for January, we will grab the year and subtract one. If the month is `12` for December, do the opposite and add one.

Similar to the `date-icons` in the `DateIndicator` component, this one adds the data-attribute `data-date` to the previous and following month elements.

```javascript
...  
<div className="bae-month-indicator">  
  <h4 data-date={monthSet.prev} onClick={changeMonth}>  
    {monthsFull[getMonth(monthSet.prev)]}  
  </h4>  
  <h3>{monthsFull[getMonth(monthSet.current)]}</h3>  
  <h4 data-date={monthSet.next} onClick={changeMonth}>  
    {monthsFull[getMonth(monthSet.next)]}  
  </h4>  
</div>  
...
```

As you can see, these two elements also appear to have a `onClick` event listener calling the function `changeMonth` . Simlar to the callback function in the `DateIndicator` , it’s changing the state `selectDate` by calling `setSelectDate` .

Bit problematic though. The name `changeMonth` seems a bit misleading, because we’re technically changing the whole date of the `selectDate` state and this code is duplicated! Moments like these are where you should consider refactoring this to reduce duplicated code and change names of functions to be more accurate with its intended behavior.

For now, let’s change the name to `changeDate` and leave it in the component. In cases like these, there are many opinions on whether to refactor the duplicate code. However, for a small project, I prefer to keep callback functions in the component where they are used. This is something that should be reconsidered as a project gets larger over time, but this should be fine for now.

![Component with Styling](https://dev-to-uploads.s3.amazonaws.com/i/8o13l2j41sd9r7apl46s.png)
Component with Styling

Not bad right? By now you should have a functioning React calendar component that changes dates in the `CalendarHeader` and `MonthIndicator` as you click the dates.

If you want to take a look at the code for the entire component, take a look at the [Github repository](https://github.com/bert-bae/bae-components/tree/master/src/components/calendar).

In the last and final part of this series, we will add some features to the component that makes it usable for others, show selected dates, as well as the styling. Some concepts we will touch upon is component re-usability, style sheet organisation, and general CSS/SASS tricks using Flex and Grid.

Hope you enjoyed reading and found it useful to inspire you to continue developing your skills with fun mini-projects!