I’m working on a booking widget using react-day-picker in a React application. I need to disable dates that are already booked so users cannot select them. I’ve fetched the booked dates from my backend, but I’m having trouble disabling these dates in the calendar.
The DayPicker should disable already booked dates and past dates, but it’s not working as expected, since i can select all future dates
Error Message:
N/A
Environment:
React: 17.0.2
react-day-picker: 8.0.0
axios: 0.21.1
How can I ensure the calendar correctly disables unavailable dates and handles date selection accurately?
Here’s my current component:
import { useContext, useEffect, useState } from "react";
import { differenceInCalendarDays, isSameDay } from "date-fns";
import axios from "axios";
import { Navigate, useParams } from "react-router-dom";
import { UserContext } from "../UserContext";
import { DayPicker } from "react-day-picker";
import "react-day-picker/dist/style.css";
function BookingWidget({ place }) {
const [checkIn, setCheckIn] = useState("");
const [checkOut, setCheckOut] = useState("");
const [numberGuests, setNumberGuests] = useState(1);
const [name, setName] = useState("");
const [mobile, setMobile] = useState("");
const [redirect, setRedirect] = useState("");
const { user } = useContext(UserContext);
const { id } = useParams();
const [alreadyBooked, setAlreadyBooked] = useState([]);
useEffect(() => {
if (user) {
setName(user.name);
}
axios.get("/booking/" + id).then((response) => {
const bookings = response.data;
const bookedDates = [];
for (let booking of bookings) {
const start = new Date(booking.checkIn);
const end = new Date(booking.checkOut);
for (let date = start; date <= end; date.setDate(date.getDate() + 1)) {
bookedDates.push(new Date(date));
}
}
setAlreadyBooked(bookedDates);
});
}, [user, id]);
let numberOfNights = 0;
if (checkIn && checkOut) {
numberOfNights = differenceInCalendarDays(
new Date(checkOut),
new Date(checkIn)
);
}
async function bookThisPlace() {
const response = await axios.post("/bookings", {
checkIn,
checkOut,
numberGuests,
name,
mobile,
price: numberOfNights * place.price,
place: place._id,
});
const bookingId = response.data._id;
setRedirect(`/account/bookings/${bookingId}`);
}
const isPastDay = (day) => {
return day < new Date();
};
const isBookedDay = (day) => {
return alreadyBooked.some(bookedDay => isSameDay(day, bookedDay));
};
const disabledDays = [
{ before: new Date() }, // Disable past days
...alreadyBooked.map(date => ({ date })) // Disable already booked days
];
if (redirect) {
return <Navigate to={redirect} />;
}
return (
<div className="bg-white shadow p-4 rounded-2xl">
<div className="text-2xl text-center mb-3">
<b>Price: {place.price} €/night</b>
</div>
<div className="border mt-4 rounded-2xl">
<div className="flex">
<div className="py-3 px-4">
<label>Check-in date: </label>
<DayPicker
mode="single"
selected={checkIn ? new Date(checkIn) : undefined}
onSelect={setCheckIn}
disabled={disabledDays}
/>
</div>
<div className="py-3 px-4 border-l">
<label>Check-out date: </label>
<DayPicker
mode="single"
selected={checkOut ? new Date(checkOut) : undefined}
onSelect={setCheckOut}
disabled={disabledDays}
/>
</div>
</div>
<div className="py-3 px-4 border-t">
<label>Guests: </label>
<input
type="number"
value={numberGuests}
onChange={(ev) => setNumberGuests(ev.target.value)}
/>
</div>
{numberOfNights > 0 && (
<div className="py-3 px-4 border-t">
<label>Your name: </label>
<input
type="text"
value={name}
onChange={(ev) => setName(ev.target.value)}
/>
<label>Phone: </label>
<input
type="tel"
value={mobile}
onChange={(ev) => setMobile(ev.target.value)}
/>
</div>
)}
</div>
<button onClick={bookThisPlace} className="primary rounded-2xl mt-4">
Book now{" "}
{numberOfNights > 0 && (
<span>{numberOfNights * place.price} €</span>
)}
</button>
</div>
);
}
export default BookingWidget;
´´´