You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

NextJS中react-calendar日期重复及当日无法选择问题求助

问题分析与修复方案

问题概述

使用NextJS、TypeScript、TailwindCSS及react-calendar开发日历组件时,遇到两个核心问题:

  • 选择日期范围时,下月除已选日期及跨月日期外的所有日期重复显示
  • 无法选择当日、当月及当年的日期

问题原因及修复方案

1. 日期重复显示问题

原因:tileContent逻辑错误,当日期不在选中范围的月份时,错误返回日期数字,导致react-calendar原生渲染的日期与自定义内容叠加显示。

修复:调整tileContent判断逻辑,仅在日期属于当前显示月份、未被选中且未过期时返回自定义内容:

tileContent={({ date, view, activeStartDate }) => {
  if (view === 'month') {
    // 判断日期是否属于当前显示的月份
    const isCurrentMonth = date.getMonth() === activeStartDate.getMonth() && date.getFullYear() === activeStartDate.getFullYear();
    if (selectedDates === null || isDateSelected(date)) {
      return null;
    } else if (date < new Date(new Date().setHours(0, 0, 0, 0))) {
      return null;
    } else if (!isCurrentMonth) {
      return null;
    } else {
      return (
        <div className="">
          {date.getDate()}
        </div>
      );
    }
  }
  return null;
}}

2. 无法选择当日及近期日期问题

原因:tileDisabled中使用date <= new Date(),会将当日未过的时间段也判定为不可选,导致整个当日无法选择。

修复:将当日时间部分清零后再比较,确保当日可被选中:

tileDisabled={({ date }) => {
  const today = new Date();
  today.setHours(0, 0, 0, 0);
  return date < today;
}}

完整修正后的核心代码片段

import React, { useState } from 'react';
import Calendar from 'react-calendar';
import ReservationModal from './ReservationModal';

import '../calendar/CusCalendar.css';

const CusCalendar: React.FC = () => {
  const [selectedDates, setSelectedDates] = useState<Date[] | Date | null>(null);
  const [showCalculation, setShowCalculation] = useState(false);
  const [showReservationPopup, setShowReservationPopup] = useState(false);
  const [currentMonth, setCurrentMonth] = useState(new Date());

  const startDate = Array.isArray(selectedDates) ? selectedDates[0] : null;
  const endDate = Array.isArray(selectedDates) ? selectedDates[1] : null;
  const pricePerNight = 100;

  const nights = startDate && endDate ? Math.floor((endDate.getTime() - startDate.getTime()) / (1000 * 3600 * 24)) : 0;
  const totalPrice = nights * pricePerNight;

  const isDateSelected = (date: Date) => {
    return startDate && endDate && date >= startDate && date <= endDate;
  };

  const handleDateChange = (date: Date | Date[]) => {
    if (!startDate || (startDate && endDate)) {
      setSelectedDates(Array.isArray(date) ? date : [date]);
    } else {
      if (Array.isArray(date) && date[0] >= startDate) {
        setSelectedDates(date);
      } else {
        setSelectedDates(Array.isArray(date) ? date : [date]);
      }
    }
  };

  const handleCalculate = () => {
    if (startDate && endDate) {
      const numberOfNights = Math.floor(
        (endDate.getTime() - startDate.getTime()) / (1000 * 3600 * 24)
      );
      const totalPrice = numberOfNights * pricePerNight;
      setShowCalculation(true);
    }
  };

  const handleClearDates = () => {
    setSelectedDates(null);
  };

  const handleReserve = () => {
    if (startDate && endDate) {
      setShowReservationPopup(true);
    }
  };

  const handleReserveClick = () => {
    setShowReservationPopup(true);
  };

  const handleMonthChange = (newMonth: Date) => {
    setCurrentMonth(newMonth);
  };

  return (
    <div className="text-start border-4 rounded-[30px] solid border-[#ebebeb] w-[478px] h-full">
      {!showCalculation ? (
        <div>
          <div className="text-[28px] font-bold text-[#313131] leading-normal uppercase pl-7 pt-7 pb-3">SELECT CHECK-IN DATE</div>
          <div className="text-[18px] font-normal leading-normal text-[#969696] pl-7 pb-7">Add your travel dates for exact pricing</div>
        </div>
      ) : null}
      <div className="mx-7">
        {!showCalculation ? (
          <Calendar
            onChange={handleDateChange as any}
            selectRange={true}
            tileContent={({ date, view, activeStartDate }) => {
              if (view === 'month') {
                const isCurrentMonth = date.getMonth() === activeStartDate.getMonth() && date.getFullYear() === activeStartDate.getFullYear();
                if (selectedDates === null || isDateSelected(date)) {
                  return null;
                } else if (date < new Date(new Date().setHours(0, 0, 0, 0))) {
                  return null;
                } else if (!isCurrentMonth) {
                  return null;
                } else {
                  return (
                    <div className="">
                      {date.getDate()}
                    </div>
                  );
                }
              }
              return null;
            }}
            tileDisabled={({ date }) => {
              const today = new Date();
              today.setHours(0, 0, 0, 0);
              return date < today;
            }}
          />
        ) : null}
      </div>
      {/* 剩余部分代码保持不变 */}
    </div>
  );
};

export default CusCalendar;

补充说明

  • 日期重复问题:通过activeStartDate判断当前显示月份,确保自定义内容只渲染当前月份的日期,避免与react-calendar原生跨月日期渲染重复
  • 日期选择限制问题:将当日时间清零后比较,确保当日及之后的日期均可正常选择

内容的提问来源于stack exchange,提问作者userunknown

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.10 04:56:04