Tuesday, 24 August, 2021 UTC


Summary

Today we’ll explore the differences between a React calendar and a React scheduler component so you know when to choose one over the other. We’ll also take a look at how to implement a few real-world examples.
For many years, we have used calendars in various shapes or forms. In the past, we had them hanging on the walls showing us dates and sometimes we were marking them for special occasions. However, things changed as computers and smartphones became more common. Nowadays, the concept of a calendar can be used to create a variety of applications.
The only thing is, there are different styles of calendar components with various features. It can be confusing which is the most appropriate solution for your app. So what are the basic differences between a React calendar and a React scheduler? Let’s walk through these differences, and then look at some real-life examples.
React Calendar or React Scheduler?
A React calendar component is a good choice when you need to allow users to select a specific date, like if your app has a form field for date of birth or employee start date. Another good example would be an application that allows users to book appointments. A user would be able to select a specific date and time slot from available options. For scenarios like this, a calendar component is probably the best bet.
On the other hand, a React scheduler might be used for viewing an agenda at a glance or scheduling events and tasks. These types of components can be used to see multiple agendas at once, like your work schedule and childcare plans. What’s more, they can provide a richer overview of your tasks, as often you can view your agendas by day, week or even month.
So a calendar is more likely used for a big-picture calendar, while a scheduler is used for detailed schedules/agendas.
Now let’s explore some real examples of how these two different types of components can be used, even looking at some actual code implementation. The full code examples are available in this GitHub repository. You can also try out the interactive example below.
Project Setup
Before we dive in deeper, let’s set up a React project. To quickly scaffold one, you can use Create React App or Vite. For this demo, we are going to use Create React App. You can create a React project by running one of the below commands in your terminal.
npx create-react-app my-kendo-react-calendars  
cd my-kendo-react-calendars  
npm start // or yarn start  
I’m going to use the KendoReact Calendar and Scheduler components for these demos. So we need to install a few packages that are needed for using KendoReact Calendar and Scheduler components.
Note: KendoReact is a commercial UI component library, and as a part of this you will need to provide a license key when you use the components in your React projects. You can snag a license key through a free trial or by owning a commercial license. For more information, you can head over to the KendoReact Licensing page.
// npm  
npm install @progress/kendo-react-scheduler @progress/kendo-react-popup @progress/kendo-react-dialogs @progress/kendo-react-dateinputs @progress/kendo-react-dropdowns @progress/kendo-react-inputs @progress/kendo-react-buttons @progress/kendo-date-math @progress/kendo-react-form @progress/kendo-react-intl @progress/kendo-drawing @progress/kendo-react-treeview @progress/kendo-react-animation @progress/kendo-react-common @progress/kendo-licensing @progress/kendo-theme-default  
Next, replace the contents of App.js and App.css files.
src/App.js
import '@progress/kendo-theme-default/dist/all.css';  
import "./App.css";  
function App() {  
return <div className="App"></div>;  
}  
  
export default App;  
src/App.css
.App {  
max-width: 40rem;  
margin: 2rem auto;  
}  
That’s it for the project setup.
React Calendar
A React calendar component can be used when you need to allow users to select a specific date. A very common example of using a calendar is for picking your date of birth. Users need to be able to select the year, month and day they were born.
This can be achieved by using a simple input, but a calendar component can provide a better user experience. It will also work better on touch-screen devices, as you don’t have to type anything. Instead, you only need to click or touch a few times to select the date you want.

Selecting a Date of Birth

Let’s start by implementing a simple calendar that will allow users to select their date of birth. We will need to implement the Calendar component from the @progress/kendo-react-dateinputs library. Below you can see the code for it.
src/components/calendar/PickDateOfBirth.js
import { Calendar } from "@progress/kendo-react-dateinputs";  
import { useState } from "react";  
  
const PickDateOfBirth = props => {  
const [date, setDate] = useState(null);  
  
return (  
<div className="k-my-8">  
<div className="k-mb-6 k-font-weight-bold">Date of birth</div>  
  
<Calendar value={date} onChange={e => setDate(e.value)} />  
<div className="k-mt-4">Selected date: {date?.toDateString()}</div>  
</div>  
);  
};  
  
export default PickDateOfBirth;  
We have one useState to store the selected date. We pass value and onChange props to the Calendar component to keep the state in sync. Finally, we render the selected date below the Calendar component. We also need to update the App.js file to include our PickDateOfBirth component.
src/App.js
import "@progress/kendo-theme-default/dist/all.css";  
import "./App.css";  
import PickDateOfBirth from "./components/calendar/PickDateOfBirth";  
  
function App() {  
return (  
<div className="App">  
<PickDateOfBirth />  
</div>  
);  
}  
  
export default App;  
As the gif below shows, we have a beautiful calendar working out of the box, and we can save the date selected by a user. This example is very simple, so let’s create something a bit more complex.

Book a Driving Lesson

The Calendar component can be composed with other functionality to create more complex features.
Imagine you want to book a driving lesson with your instructor. First, you would need to be able to select the day on which you want to drive. After that, you should be presented with a list of time slots that the instructor has available. Finally, you should be able to select one of the time slots. Below you can see the code for implementing that functionality.
src/components/calendar/BookDrivingSlot.js
import { Calendar } from "@progress/kendo-react-dateinputs";  
import { useEffect, useRef, useState } from "react";  
  
const times = [  
"08:00 - 10:00",  
"10:00 - 12:00",  
"12:00 - 14:00",  
"14:00 - 16:00",  
"16:00 - 18:00",  
"18:00 - 20:00",  
];  
  
const getRandomNumInRange = (min, max) => {  
return Math.floor(Math.random() * (max - min) + min);  
};  
  
const pickSlotTimes = times => {  
// Get a random number that will indicate how many time slots we pick  
const timesToPick = getRandomNumInRange(0, times.length);  
  
// If the random picked is the maximum possible then return all times  
if (timesToPick === times.length - 1) {  
return times;  
}  
  
let timesPicked = [];  
  
// Loop until we have picked specified number of times  
while (timesToPick !== timesPicked.length - 1) {  
// Get a new index and time  
const index = getRandomNumInRange(0, times.length);  
const selectedTime = times[index];  
// If we already picked that time we continue  
// as we don't want duplicated  
if (timesPicked.includes(selectedTime)) continue;  
// Keep the time  
timesPicked.push(selectedTime);  
}  
  
// We need to sort the times, as they may not be in a correct order  
return timesPicked.sort();  
};  
  
const BookDrivingSlot = props => {  
const [bookingDate, setBookingDate] = useState(null);  
const [selectedTimeSlot, setSelectedTimeSlot] = useState(null);  
const [bookingTimes, setBookingTimes] = useState([]);  
const timeSlotCacheRef = useRef(new Map());  
  
useEffect(() => {  
// Bail out if there is no date selected  
if (!bookingDate) return;  
  
// Get time slots from cache  
let newBookingTimes = timeSlotCacheRef.current.get(  
bookingDate.toDateString()  
);  
  
// If we have no cached time slots then pick new ones  
if (!newBookingTimes) {  
newBookingTimes = pickSlotTimes(times);  
// Update cache with new time slots for the selected date  
timeSlotCacheRef.current.set(bookingDate.toDateString(), newBookingTimes);  
}  
  
setBookingTimes(newBookingTimes);  
}, [bookingDate]);  
  
const onDateChange = e => {  
setSelectedTimeSlot(null);  
setBookingDate(e.value);  
};  
  
return (  
<div className="k-my-8">  
<div className="k-mb-4 k-font-weight-bold">Book driving slot</div>  
  
<div className="k-flex k-display-flex k-mb-4">  
<Calendar value={bookingDate} onChange={onDateChange} />  
<div className="k-ml-4 k-display-flex k-flex-col">  
{bookingTimes.map(time => {  
return (  
<button  
key={time}  
className="k-button k-mb-4"  
onClick={e => setSelectedTimeSlot(time)}  
>  
{time}  
</button>  
);  
})}  
</div>  
</div>  
  
{bookingDate && selectedTimeSlot ? (  
<div>  
Selected slot: {bookingDate.toDateString()} at {selectedTimeSlot}  
</div>  
) : null}  
</div>  
);  
};  
  
export default BookDrivingSlot;  
Let’s digest the code we have in the BookDrivingSlot component. First, we define a list of possible time slots available for the day. These will be picked at random when a user selects a date in the calendar. The time slots would normally come from a database, but this will suffice for our example.
Next we have getRandomNumInRange and pickSlotTimes functions. The former is quite self-explanatory, as it returns a random number between the min and max passed. The latter does two things. First, it generates a random number that will indicate how many time slots we will have for the selected day. For example, if the generated number is 2, then there will be two time slots available for the day. If the number generated is the same as the amount of time slots in the times array, then the times array is returned.
// Get a random number that will indicate how many time slots we pick  
const timesToPick = getRandomNumInRange(0, times.length);  
  
// If the random picked is the maximum possible then return all times  
if (timesToPick === times.length - 1) {  
return times;  
}  
Otherwise, it will loop until it finds the required number of time slots and then will return them sorted.
let timesPicked = [];  
  
// Loop until we have picked specified number of times  
while (timesToPick !== timesPicked.length - 1) {  
// Get a new index and time  
const index = getRandomNumInRange(0, times.length);  
const selectedTime = times[index];  
// If we already picked that time we continue  
// as we don't want duplicated  
if (timesPicked.includes(selectedTime)) continue;  
// Keep the time  
timesPicked.push(selectedTime);  
}  
  
// We need to sort the times, as they may not be in a correct order  
return timesPicked.sort();  
We have a few stateful values to store the selected date: available time slots and a selected time slot. Besides that, we have a timeSlotCacheRef that is used to cache time slots for selected dates, so we don’t have to recompute them multiple times.
The useEffect will run once when the component is mounted and then every time the booking date changes. If there is no booking date, we bail out. Otherwise, we get booking time slots from the cache or pick new ones at random and update the cache.
useEffect(() => {  
// Bail out if there is no date selected  
if (!bookingDate) return;  
  
// Get time slots from cache  
let newBookingTimes = timeSlotCacheRef.current.get(  
bookingDate.toDateString()  
);  
  
// If we have no cached time slots then pick new ones  
if (!newBookingTimes) {  
newBookingTimes = pickSlotTimes(times);  
// Update cache with new time slots for the selected date  
timeSlotCacheRef.current.set(bookingDate.toDateString(), newBookingTimes);  
}  
  
setBookingTimes(newBookingTimes);  
}, [bookingDate]);  
Finally, the component renders the calendar, buttons with available time slots, and the selected date and time.
Now, update the App.js file to include the BookDrivingSlot component.
src/App.js
import "@progress/kendo-theme-default/dist/all.css";  
import "./App.css";  
import PickDateOfBirth from "./components/calendar/PickDateOfBirth";  
import BookDrivingSlot from "./components/calendar/BookDrivingSlot";  
  
function App() {  
return (  
<div className="App">  
<PickDateOfBirth />  
<hr className="k-my-8" />  
<BookDrivingSlot />  
</div>  
);  
}  
  
export default App;  
The gif below shows the functionality. As you can see, thanks to the KendoReact Calendar component, we can implement useful functionality quite easily.
A React calendar component is a good choice when you need to allow users to select a specific date. It can be used in conjunction with other components and elements to create richer and more complex functionality. Here we combined it with time slot buttons, but there are other use cases for it. For instance, it can be used together with input fields to create a date picker and date range picker. To see what else you can do with the Calendar component, you can check out its documentation.
React Scheduler
We have covered examples of using a React calendar component. Now, let’s explore using a React scheduler component. The Scheduler component offered by KendoReact, as the name suggests, allows users to schedule events. It offers a lot of useful functionality and can be used to create a variety of features. A good comparison is Google Calendar, which allows users to schedule tasks, events and reminders. Some other examples include event and booking management systems or work meeting schedules.
To showcase how the Scheduler can be used, we will implement a meeting room scheduler. Imagine a large office where there are a lot of employees, but only three meeting rooms. Employees should be able to reserve a room ahead of time to avoid collisions with others who would also want to meet and talk. Let’s start by creating the RoomScheduler component.
src/components/scheduler/RoomScheduler.js
import {  
Scheduler,  
TimelineView,  
DayView,  
WeekView,  
MonthView,  
AgendaView,  
} from "@progress/kendo-react-scheduler";  
import { useState } from "react";  
import { guid } from "@progress/kendo-react-common";  
  
const meetingRooms = {  
name: "Meeting Room",  
data: [  
{  
text: "Blue room",  
value: 1,  
color: "blue",  
},  
{  
text: "Red room",  
value: 2,  
color: "red",  
},  
{  
text: "Green room",  
value: 3,  
color: "green",  
},  
],  
field: "RoomID",  
valueField: "value",  
textField: "text",  
colorField: "color",  
};  
  
const compareById = matchingItem => item => matchingItem.id === item.id;  
  
const RoomScheduler = props => {  
const [data, setData] = useState([]);  
  
const onDataChange = ({ created, updated, deleted }) => {  
// Add a unique id to each new item  
const newItemsWithIds = created.map(item => ({  
...item,  
id: guid(),  
}));  
  
setData(dataState =>  
dataState.reduce((acc, item) => {  
// Skip the item if it was deleted  
if (deleted.find(compareById(item))) return acc;  
// Push the updated item or current item  
acc.push(updated.find(compareById(item)) || item);  
return acc;  
}, newItemsWithIds)  
);  
};  
  
return (  
<div className="k-my-8">  
<div className="k-mb-4 k-font-weight-bold">Book a room</div>  
<Scheduler  
editable  
data={data}  
onDataChange={onDataChange}  
resources={[meetingRooms]}  
>  
<TimelineView />  
<DayView />  
<WeekView />  
<MonthView />  
<AgendaView />  
</Scheduler>  
</div>  
);  
};  
  
export default RoomScheduler;  
First, we have defined the meetingRooms variable, which specified the details of rooms available in the office. Besides that, we could add more fields, such as whom the room was booked by, attendees and more, but just meeting rooms will do for this example.
The KendoReact Scheduler component offers five default views:
  • Timeline
  • Day
  • Week
  • Month
  • Agenda
There is no need to use all of them at once. We could just use the day, week and month views, or the other two.
Any time the data is changed by the user, the handler receives an object with properties, such as created, updated and deleted. These store items that were modified, so in the onDataChange handler, we add a unique ID for the items that were created, and then we update or delete the rest of the items where applicable.
Last but not least, add the RoomScheduler component in the App.js file.
src/App.js
import "@progress/kendo-theme-default/dist/all.css";  
import "./App.css";  
import PickDateOfBirth from "./components/calendar/PickDateOfBirth";  
import BookDrivingSlot from "./components/calendar/BookDrivingSlot";  
import RoomScheduler from "./components/scheduler/RoomScheduler";  
function App() {  
return (  
<div className="App">  
<PickDateOfBirth />  
<hr className="k-my-8" />  
<BookDrivingSlot />  
<hr className="k-my-8" />  
<RoomScheduler />  
</div>  
);  
}  
  
export default App;  
Below you can see the scheduler component in action.
There are many more features available than what’s showcased in this simple example, so definitely check out the KendoReact Scheduler documentation.
Wrap-up
There we have it. We have covered basic differences between a React calendar and a React scheduler component. As you have seen, the React calendar component can be used to implement features that allow users to select specific dates. On the other hand, the React scheduler is a good choice for meeting scheduling or agenda-like applications.
If you’re adding such components to your own app, I recommend checking out using third-party components. It’s not an easy feat to implement well-working calendar and scheduler components, so using ready-made solutions like the ones found in KendoReact is a good idea, as you get nice-looking and feature-rich components out of the box. What’s more, both components provide support for dealing with time zones, localization and internationalization, so they can save you a lot of time and effort.