reactjstypescriptstatereact-usememo

How can I only assign a previous value to state of my datepicker if the new one is invalid?


I am dealing with a bug in a library I'm using that will sometimes create invalid values that my code will need, crashing the page. I would like to only apply that new value (the result of useDateRangePickerState) if it is valid. If not, I do not want to assign that value to my state. How can I do this?

export const DateRangePicker: FC<AriaDateRangePickerProps<DateValue>> = memo((props) => {

  const defaultNowTime = {timeValue}
  const [nowTime, setNowTime] = useState(defaultNowTime);

  const newstate = useDateRangePickerState({
    ...props,
    defaultValue: {
      start: defaultNowTime.set({ second: 0, minute: 0, millisecond: 0, hour: 0 }),
      end: defaultNowTime
    },
    hideTimeZone: true,
    shouldCloseOnSelect: false,
  });

  //once a valid 'newstate' is created, cache it with useMemo here?
  //cachedOldState = ...
  //only use the valid to assign state if its valid! *******************
  const state = (newstate is valid) ? newstate : cachedOldState
...

Solution

  • If the useDateRangePickerState hook part is something that you control then you will want to stop invalid values there before they bubble up. But if it's part of the buggy library then you can make it work.

    You've got the right idea here:

    //once a valid 'newstate' is created, cache it with useMemo here?
    //cachedOldState = ...
    //only use the valid to assign state if its valid! *******************
    const state = (newstate is valid) ? newstate : cachedOldState
    

    We need to store the previous valid state somewhere. I would use a useState for that.

    Then we need to check changes from the date picker and see if they are valid. I recommend a useEffect hook with a dependency on the newstate. This will run every time that the newstate changes.

    If the newstate is valid we will call setState and that way we keep our "previous valid state" value in sync.

    const newstate = useDateRangePickerState({ /* ... */ });
    
    const [validDate, setValidDate] = useState(initialValue);
    
    useEffect(() => {
       if (isValid(newstate)) {
           setValidDate(newstate);
       }
    }, [newstate]);
    

    As a sidenote, I'm really confused by the code in your question. defaultNowTime is stored in state so isn't it a mutation of state to call defaultNowTime.set in the defaultValue?