I am having issue with the MyCalendar component below which used the react-calendar. I have logged selectedValue
for debugging purposes.
If I render the MyCalendar
component on its own, then its internal state selectedValue
tracks the selected date in the Calendar
appropriately, and the Calendar
highlights the selected date.
However, if I use the MyCalendar
component within DatePicker
, then the value inside the DatePicker
box is correct, however the state within the child MyCalendar
component is always null
for some reason, and thus the selected date is not highlighted within the Calendar. It seems that the selectedValue
state in MyCalendar
is reinitialised to null
when a new date is picked in DatePicker
.
Would anyone be able to help me understand what the problem is here? I would like MyCalendar
to have its own state rather than taking Calendar
prop value
from a parent component.
const MyCalendar: React.FC<PropsCalendar> = ({
onDateSelect,
selectRange = false,
}) => {
const [selectedValue, setSelectedValue] = useState<Value>(null);
console.log(selectedValue)
const handleDateChange = (value: Value) => {
setSelectedValue(value);
if (onDateSelect) {
onDateSelect(value);
}
};
return (
<Calendar
onChange={handleDateChange}
value={selectedValue}
calendarType="gregory"
selectRange={selectRange}
/>
);
};
export const DatePicker: React.FC<PropsDatePicker> = ({
selectRange = false,
}) => {
const [isSelectingDates, setIsSelectingDates] = useState(false);
const [value, setValue] = useState<Value>(null);
const handleChange = (value: Value) => {
setValue(value);
if (Array.isArray(value)) {
// selecting a date range
if (value[0] && value[1]) setIsSelectingDates(false);
} else {
setIsSelectingDates(false);
}
};
return (
<>
<div
onClick={(event) =>setIsSelectingDates(!isSelectingDates)}}
>{value}</div>
{isSelectingDates && (
<MyCalendar
onDateSelect={handleChange}
selectRange={selectRange}
/>
)}
</>
);
};
your MyCalendar component is being conditionally rendered. so when it stops being rendered, it loses its state. you may need the state temporarily in the calendar if selecting a date range, but after the selection is made, only the parent component should hold the state.
my solution was to give the MyCalendar component an initial state passed in from the parent and call the "onDateSelect" only when the user has completed the selection. if a range, when the value is a valid pair of dates. if not, onChange.
this way, the final value lives in the parent component, which is always rendered and where state can remain.
const MyDatePicker = ({ selectRange = false }) => {
const [isSelectingDates, setIsSelectingDates] = useState(false);
const [value, setValue] = useState(null);
const handleChange = (val) => {
setValue(val);
setIsSelectingDates(false);
};
useEffect(() => { console.log('datepicker val: ', value); }, [value]);
return (
<>
<div onClick={() => { setIsSelectingDates(!isSelectingDates); }}>
{value ? 'value selected' : 'No Date Selected'}
</div>
{isSelectingDates &&
<MyCalendar
onDateSelect={handleChange}
selectRange={selectRange}
initialValue={value}
/>}
</>);
};
const MyCalendar = ({ onDateSelect, initialValue, selectRange = false }) => {
const [selectedValue, setSelectedValue] = useState(initialValue);
const handleChange = (val) => {
setSelectedValue(val);
if (Array.isArray(val)) {
if (val[0] && val[1]) { onDateSelect(val); }
} else {
onDateSelect(val);
}
};
useEffect(() => { console.log('calendar: ', selectedValue); }, [selectedValue]);
return (
<Calendar
onChange={handleChange}
value={selectedValue}
calendarType='gregory'
selectRange={selectRange}
/>);
};