Range mode has its own internal state and does not respect the controlled state
Closed this issue ยท 4 comments
Code
import { useState } from 'react';
import { DateRange, DayPicker } from 'react-day-picker';
export function ControlledCalendar() {
const [selected, setSelected] = useState<DateRange | undefined>();
return (
<DayPicker
mode="range"
selected={selected}
onSelect={(selected) => {
// setSelected(selected);
}}
/>
);
}
Expected Behavior
Since the component is being used in "controlled" mode, it should use the state passed via "selected" prop. In the example above, the selected state is purposely not updated. It was expected that the calendar would not work, since the selected values aren't being updated.
I was trying to implement a behavior where if the range is already selected, and the user click on a new day, the range is reset:
import { useState } from 'react';
import { DateRange, DayPicker } from 'react-day-picker';
export function ControlledCalendar() {
const [selected, setSelected] = useState<DateRange | undefined>();
function handleOnSelect(range: DateRange, triggerDate: Date) {
if (selected?.from && selected?.to) {
setSelected({
from: triggerDate,
to: undefined,
});
return;
}
setSelected(selected);
}
return <DayPicker mode="range" required selected={selected} onSelect={handleOnSelect} />;
}
Since the internal state is updated via useEffect, it causes a small flicker. The sequence of events is:
User click -> internal range state updated -> onSelect callback -> external state updated (range reset) -> internal state is updated via useEffect due external state change.
Actual Behavior
The calendar has its own internal state. The external state is updated via useEffect.
Video
Tab-App.tsx.nodebox.CodeSandbox.mp4
PS: I throttled the CPU in 6x to make it more noticeable
thanks @matheusmb for your detailed report ! you are right the controlled vs uncontrolled state isn't the ideal here.
The code should read something like:
const handleSelect = (value: string) => {
if (onSelect) {
onSelect(value); // If the component is controlled, trigger the onSelect callback
} else {
setInternalSelected(value); // If the component is uncontrolled, update the internal state
}
};
Hey @gpbl,sure thing! I like the way Chakra-UI implemented it, it might be useful to take a look at:
Hey this is more difficult than I thought. Please have patience...
@matheusmb to help - could you create a (failing) test case, reproducing the issue? ๐๐ฝ