Search code examples
javascriptreactjstypescriptant-design-pro

React AntDesign Calendar Component selects last selected day on month change


I want to achieve that when the user clicks on a day, the selected day is saved in the state. However, I have the problem that when the month changes, the previously selected day is also saved in the state along with the currently selected month. When I log the onSelect event to the console and change the month, I receive 2 Dayjs objects.

Here's a Sandbox link: https://codesandbox.io/s/antd-reproduction-template-forked-0ihci4?file=/index.tsx

The code:

    const [selectedDays, setSelectedDays] = React.useState<Dayjs[]>([])
    
    console.log(selectedDays)
    
    
    const dateCellRender = (value: Dayjs) => {
    
        // give selected days a different color
    
        if (selectedDays.some(day => day.isSame(value, 'day'))) {
            return (
                <div className=" h-28 m-1 rounded  hover:bg-blue-100 bg-blue-200 transition-all" >
                    {value.date()}
                </div>
            )
        } else {
            return (
                <div className=" h-28  rounded  border-t hover:bg-blue-100  transition-all">
                    {value.date()}
                </div>
            )
        }
    };
    
    return <div className='m-10'><Calendar value={undefined} onSelect={(e) => setSelectedDays(prev => {
        // if the day is already selected, remove it from the array of selected days with isSame
        if (prev.some(day => day.isSame(e, 'day'))) {
            return prev.filter(day => !day.isSame(e, 'day'))
        } else {
            return [...prev, e]
        }
    })} fullCellRender={dateCellRender} /></div>
};

I tried to use different props like onChange onSelect, onChange, etc.. but there are always 2 console logs. I expected that there is no new selection when I change the month.


Solution

  • You need to define additional state variable that would keep the state of the selected date in the calendar. You can assign it by default to the current date with by just creating dayjs object

      const [selectedDays, setSelectedDays] = React.useState<Dayjs[]>([]);
      // this is a new state variable
      const [calendarDate, setCalendarDate] = React.useState<Dayjs>(dayjs());
    

    Then what you have to do is just check if the month or year changed in onSelect handler. I checked that this one is also called when you use the select for month and year.

    What you have to do in that check, is just clearing your selectedDays state by assigning empty array to it and do not execute the function further by using return statement in that if block. That way every time you change month or the year, you won't have any selected days.

    onSelect function code:

           onSelect={(e) => {
              // this is the new if statement that clears the state when month or year changes
              if (!calendarDate.isSame(e, 'month') || !calendarDate.isSame(e, 'year')) {
                setCalendarDate(e);
                setSelectedDays([]);
                return;
              }
    
              setSelectedDays((prev) => {
                // if the day is already selected, remove it from the array of selected days with isSame
                if (prev.some((day) => day.isSame(e, "day"))) {
                  return prev.filter((day) => !day.isSame(e, "day"));
                } else {
                  return [...prev, e];
                }
              })
            }