Without testing your approach, I imagine the issue with it, as you have already guessed, is that you are re-rendering the whole calendar everytime a new day is hovered, which causes the flickering.
To avoid this, we need to use vanilla js, as in the suggestion made by @user25539973. I have done something similar to him, but it should be more reliable, see below.
One caviat though is that you'll need to modify the Calendar component of the primereact library so that the date cells contain the information of their day, month and year. Alternatively, you could implement some logic to find them out based on the header of the month view and on the week of the date cell. I have just created a PR for this information to be available in the next primereact versions: https://github.com/primefaces/primereact/pull/7650. Hopefully, it will be merged soon.
export default function DateRangeSelector() {
const [selectedDates, setSelectedDates] = useState<(Date | null)[]>([]);
const [rangeStart, setRangeStart] = useState<Date | null>(null);
const calendarRef = useRef<Calendar>(null); // Ref to the Calendar DOM
const handleDateSelect = (selectedDates: (Date | null)[]) => {
setSelectedDates(selectedDates); // Finalize the range
if (!rangeStart) {
setRangeStart(selectedDates[0]); // Set the start date
} else {
setRangeStart(null); // Reset start date
}
};
const handleDateHover = (event: React.MouseEvent<HTMLTableCellElement>) => {
if (!calendarRef.current) return
if (!rangeStart || selectedDates[1]) return
if (!(event.target instanceof HTMLTableCellElement)) return
const hoveredDay = event.target.getAttribute('data-p-day');
const hoveredMonth = event.target.getAttribute('data-p-month');
const hoveredYear = event.target.getAttribute('data-p-year');
if (hoveredDay === null || hoveredMonth === null || hoveredYear === null) return;
const hoveredDate = new Date(parseInt(hoveredYear), parseInt(hoveredMonth), parseInt(hoveredDay));
const minDate = rangeStart < hoveredDate ? rangeStart : hoveredDate;
const maxDate = rangeStart > hoveredDate ? rangeStart : hoveredDate;
// Remove existing highlights
const element = calendarRef.current.getOverlay()
const allCells = element.querySelectorAll('.p-datepicker-calendar td');
allCells.forEach((cell) => cell.firstElementChild?.classList.remove('p-highlight'));
// Highlight dates in the range
allCells.forEach((cell) => {
const day = cell.getAttribute('data-p-day');
const month = cell.getAttribute('data-p-month');
const year = cell.getAttribute('data-p-year');
if (day === null || month === null || year === null) return;
const cellDate = new Date(parseInt(year), parseInt(month), parseInt(day));
if (cellDate >= minDate && cellDate <= maxDate) cell.firstElementChild?.classList.add('p-highlight');
});
};
return (
<div>
<h3>Select Date Range</h3>
<Calendar
ref={calendarRef} // Attach ref to access DOM
value={selectedDates}
onChange={(e) => { if (e.value) handleDateSelect(e.value) }}
selectionMode="range" numberOfMonths={2}
readOnlyInput
pt={{day: { onMouseEnter: (event ) => handleDateHover(event) }}}
/>
</div>
);
}