Creating a Reservation Calendar with React

Ryan See
4 min readFeb 17, 2023

--

The reservation calendar (or BookingCalendar ) component is a React-based calendar that allows users to select dates for booking. It includes the ability to retrieve existing reservations and block off those dates on the calendar, as well as select and highlight date ranges for new reservations.

Getting Started

The first step in building this component is to import the necessary modules. In this case, we need React for building the component, useState and useEffect hooks for handling state and lifecycle methods, react-calendar for displaying the calendar UI, and axios for making HTTP requests to retrieve data.

import React, { useState, useEffect } from 'react';
import Calendar from 'react-calendar';
import axios from 'axios';
import "../Styling/BookingCalendar.css"

State and HTTP Requests

The component has several pieces of state:

  • reservations: An array of objects representing existing reservations retrieved via an HTTP request
  • blockedDates: An array of objects representing the start and end dates of existing reservations, formatted as from and to
  • start: A Date object representing the start date of a new reservation
  • end: A Date object representing the end date of a new reservation
const [reservations, setReservations] = useState([]);
const [blockedDates, setBlockedDates] = useState([]);
const [start, setStart] = useState(null);
const [end, setEnd] = useState(null);

The reservations are retrieved using the useEffect hook, which runs once when the component is mounted. We make an HTTP GET request to retrieve the reservations, then set the state using the setReservations function:

useEffect(() => {
axios.get('/reservations')
.then(response => {
setReservations(response.data);
});
}, []);

We also have another useEffect hook that runs whenever the reservations state changes. This hook converts the reservations data into an array of objects representing the start and end dates of each reservation, then sets the blockedDates state:

useEffect(() => {
const dates = reservations.map(reservation => {
return {
from: new Date(reservation.start_date),
to: new Date(reservation.end_date),
};
});
setBlockedDates(dates);
}, [reservations]);

Selecting and Highlighting Dates

The BookingCalendar component uses the Calendar component from react-calendar to display the UI for selecting and highlighting dates.

The tileClassName function is used to determine the class name for each date tile, based on whether the date is blocked by an existing reservation or part of a selected date range:

const tileClassName = ({ date, view }) => {
if (view === 'month') {
if (isBlocked(date)) {
return 'blocked';
} else if (
start &&
!end &&
start.toDateString() === date.toDateString()
) {
return 'selected start';
} else if (
start &&
end &&
date >= start &&
date <= end
) {
return 'selected range';
} else {
return 'available';
}
}
};

The isBlocked function is used to check if a date is blocked by an existing reservation. It returns true if the given date is within the start and end dates of any existing reservation:

const isBlocked = date =>
blockedDates.some(d => date >= d.from && date <= d.to);

The handleSelect function is called every time the user clicks on a date on the calendar. This function will be responsible for selecting dates and updating the state of start and end accordingly. If the selected date is blocked, the function will return and nothing will happen.

Here is what the handleSelect function does:

  • If start is not set, it means we are selecting the first date of the range. So, we set start to the selected date and end to null.
  • If start is set but end is not, it means we are selecting the second date of the range. If the selected date is after the start date, we set end to the selected date. Otherwise, we do nothing.
  • If both start and end are set, it means the range has been selected and we need to clear the selection. So, we set start and end to null.

After updating the start and end state, the onSelectDate function is called to pass the selected range to the parent component.

Finally, the tileClassName function is passed to the Calendar component. This function is used to set the class name of each tile on the calendar based on its date and the current selection state. It returns different class names depending on whether the date is available, blocked, or selected. This allows us to style the tiles accordingly using CSS.

The CSS styles for the calendar are defined in the style tag at the bottom of the component. The styles define the size and appearance of the calendar, as well as the colors for each tile class.

Implementation

To use this component in your project, you can simply import it and include it in your JSX code, passing in the required props. Here is an example of how to use the component:

import BookingCalendar from './BookingCalendar';
function Form() {
const handleSelectDate = ({ start, end }) => {
console.log(`Selected range: ${start} to ${end}`);
};
return (
<div className="Form">
<BookingCalendar
selectedStart={new Date()}
selectedEnd={null}
onSelectDate={handleSelectDate}
/>
</div>
);
}

In this example, we pass in a function called handleSelectDate as the onSelectDate prop. This function will be called every time a date range is selected on the calendar. It receives an object with start and end properties that represent the selected range.

That’s it! You now have a fully functional booking calendar component that you can use in your React projects. I hope you found this article helpful in understanding how the component works and how it can be customized to suit your needs.

--

--