I have a component that works as a time picker. Also I use MUI tabs to allow user choose between the whole day range and a range that user inputs. So user can enter desired time like from 14:00 to 20:00 and if they click Whole Day tab time picker interval becomes 00:00 and 23:59 and inputs become disabled.
To listen for chosen tab I have come up with a useEffect which works fine until I add all the dependencies. React Hook useEffect has missing dependencies: 'formTimeValues' and 'savedTime'. Either include them or remove the dependency array
useEffect(() => {
if (wholeDay) {
setSavedTime(formTimeValues);
setValue("defaultDeliveryWindow", wholeDayRange.defaultDeliveryWindow);
} else {
setValue("defaultDeliveryWindow", savedTime);
}
}, [setValue, wholeDay]);
But if I add all of the missing dependencies my time becomes rewritten every time and the functionality breaks. How can I refactor the useEffect or do something else to keep the functionality?
const wholeDayRange = {
defaultDeliveryWindow: {
defaultEarliestLoadTime: "00:00",
defaultLatestLoadTime: "23:59",
},
};
const TimePickerOwn = ({ wholeDay = false }) => {
const { t } = useTranslation();
const { getValues, watch, setValue } = useFormContext();
const formTimeValues = getValues()?.defaultDeliveryWindow;
const currentOption = useSelector((state) => state.orderIntake.currentOption);
const { defaultDeliveryWindow } = currentOption;
const defaultEarliestLoadTime =
formTimeValues?.defaultEarliestLoadTime ||
defaultDeliveryWindow?.defaultEarliestLoadTime;
const defaultLatestLoadTime =
formTimeValues?.defaultLatestLoadTime ||
defaultDeliveryWindow?.defaultLatestLoadTime;
const watchEarliest = watch(
"defaultDeliveryWindow.defaultEarliestLoadTime",
defaultEarliestLoadTime
);
const watchLatest = watch(
"defaultDeliveryWindow.defaultLatestLoadTime",
defaultLatestLoadTime
);
const [savedTime, setSavedTime] = useState({
defaultEarliestLoadTime,
defaultLatestLoadTime,
});
const [earliestIndex, setEarliestIndex] = useState(
findCurrentIndex(watchEarliest)
);
const [latestIndex, setLatestIndex] = useState(findCurrentIndex(watchLatest));
useEffect(() => {
if (wholeDay) {
setSavedTime(formTimeValues);
setValue("defaultDeliveryWindow", wholeDayRange.defaultDeliveryWindow);
} else {
setValue("defaultDeliveryWindow", savedTime);
}
}, [setValue, wholeDay]);
useEffect(() => {
const timeOptionsLastIndex = timeOptions.length - 1;
if (earliestIndex === latestIndex) {
if (latestIndex === timeOptionsLastIndex) {
setEarliestIndex(timeOptionsLastIndex - 1);
} else {
setLatestIndex((prev) => prev + 1);
}
} else if (earliestIndex > latestIndex) {
setLatestIndex(earliestIndex + 1);
} else if (latestIndex === 0) {
setLatestIndex(earliestIndex + 1);
}
}, [earliestIndex, latestIndex]);
useEffect(() => {
const subscription = watch(({ defaultDeliveryWindow }, { type }) => {
console.log(defaultDeliveryWindow);
if (type) return;
setEarliestIndex(
findCurrentIndex(defaultDeliveryWindow.defaultEarliestLoadTime)
);
setLatestIndex(
findCurrentIndex(defaultDeliveryWindow.defaultLatestLoadTime)
);
});
return () => subscription.unsubscribe();
}, [watch]);
return (
<>
<TimeInput
label={t("orderIntake.earliest")}
id="startTime"
time={watchEarliest}
index={earliestIndex}
setIndex={setEarliestIndex}
options={timeOptions.slice(0, -1)}
registerName="defaultEarliestLoadTime"
isDisabled={wholeDay}
/>
<Box marginBottom={2} />
<TimeInput
label={t("orderIntake.latest")}
id="endTime"
time={watchLatest}
index={latestIndex}
setIndex={setLatestIndex}
options={timeOptions.slice(earliestIndex + 1)}
registerName="defaultLatestLoadTime"
isDisabled={wholeDay}
/>
</>
);
};
Here are the tabs:
const OITimeTabs = () => {
const { t } = useTranslation();
const [tabsValue, setTabsValue] = useState(0);
const handleChange = (_, newValue) => {
setTabsValue(newValue);
};
return (
<>
<Box sx={{ width: "100%", my: 2 }}>
<StyledTabs
onChange={handleChange}
value={tabsValue}
aria-label="Tabs to choose between whole day and time range delivery"
TabIndicatorProps={{ hidden: true }}
>
<StyledTab
label={t("orderIntake.timePicker.timeRange")}
{...a11yProps(0)}
/>
<StyledTab label={t("orderIntake.wholeDay")} {...a11yProps(1)} />
</StyledTabs>
<TimePickerOwn wholeDay={!!tabsValue} />
</Box>
<Typography sx={{ fontSize: "14px", color: "#00374d", mb: 1 }}>
{`${t("orderIntake.timePicker.note")}: ${t(
"orderIntake.timePicker.noteDescription",
{
from: "01:00 PM",
to: "11:59 PM",
}
)}`}
</Typography>
</>
);
};
2
Answers
Did it like this. Seems to be working. Though I don't like repetetive declaration of
formTimeValues
and callingsetValue
from insidesetSavedTime
seems odd too.Maybe try to call setValue only when time has changed.