我正在React应用程序中使用react-day-picker开发一个预订小部件。我需要禁用已预订的日期,以便用户无法选择它们。我已从后端获取预订日期,但在日历中禁用这些日期时遇到问题。
DayPicker 应该禁用已预订的日期和过去的日期,但它没有按预期工作,因为我可以选择所有未来的日期
错误信息: 不适用
环境:
反应:17.0.2 反应日期选择器:8.0.0 axios:0.21.1
如何确保日历正确禁用不可用的日期并准确处理日期选择?
这是我当前的组件:
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;
´´´
为了确保日历正确禁用不可用的日期并准确处理日期选择,您需要解决 BookingWidget 组件中的一些问题:
正确格式化禁用天数:react-day-picker 要求以特定格式指定禁用天数。您需要将已预订的日期映射为正确的格式才能禁用。
处理日期选择:确保 DayPicker 的 onSelect 处理程序正确更新状态。
这是经过更正的更新组件:
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 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={(date) => setCheckIn(date)}
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={(date) => setCheckOut(date)}
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;