zendeskgarden/react-components

Cannot update a component X while rendering a different component DatepickerRange

pedropmedina opened this issue · 4 comments

I followed the docs and updating the state from within onChange in DatePicker is causing the error above.

Here's a snippet of the component:

const BaseDateRange: React.FC<BaseDateRangeProps> = ({ onChangeDateRange }) => {
  const [startValue, setStartValue] = useState(new Date());
  const [endValue, setEndValue] = useState(new Date());
  const [showCalendar, setShowCalendar] = useState(false);

  const isInvalid = () => compareAsc(startValue, endValue) === 1;

  const handleShowCalendar = (show: boolean) => {
    if (!show) setShowCalendar(true);
  };

  return (
    <OutsideEvent handler={() => setShowCalendar(false)}>
      <DatepickerRange
        isCompact={isCompact}
        startValue={startValue}
        endValue={endValue}
        onChange={(changes) => {
          if (onChangeDateRange) onChangeDateRange(changes);
          if (changes.startValue) setStartValue(changes.startValue);
          if (changes.endValue) setEndValue(changes.endValue);
        }}
      >
        <div className="py-2 px-4 relative">
          <div className="flex items-center mb-4">
            <div className="px-2 text-sm">Date range:</div>
            <div>
              <Field>
                <DatepickerRange.Start>
                  <StyledInput isCompact={true} onFocus={() => handleShowCalendar(showCalendar)} />
                </DatepickerRange.Start>
              </Field>
            </div>
            <div className="px-2 text-sm">to</div>
            <div className="max-w-[130px]">
              <Field>
                <DatepickerRange.End>
                  <StyledInput
                    isCompact={true}
                    validation={isInvalid() ? 'error' : undefined}
                    onFocus={() => handleShowCalendar(showCalendar)}
                  />
                </DatepickerRange.End>
                {isInvalid() && (
                  <Message validation="error">End date must occur after the start date</Message>
                )}
              </Field>
            </div>
          </div>
          {showCalendar && (
            <div className="shadow-xl max-w-min px-6 py-4 ml-2 absolute top-[100%] left-0 bg-white">
              <DatepickerRange.Calendar />
            </div>
          )}
        </div>
      </DatepickerRange>
    </OutsideEvent>
  );
};
hzhu commented

Hi @pedropmedina 👋🏽 . Could you create a minimal CodeSandbox example demonstrating the issue you're encountering, so I can investigate further?

Feel free to use Garden's CodeSandbox template or create a new sandbox example.

Hey @hzhu, sorry for the late response. Here's a link https://codesandbox.io/s/unruffled-feistel-mjj25?file=/src/App.js
Let me know if there's anything else you need - Thanks

hzhu commented

Hi @pedropmedina, my investigations indicate that this is a bug on Garden's side relating to state synchronization.

It appears that the onChange is called immediately with the controlled value — before the DatepickerRage has had a chance to complete its render. The likely cause of this is that the onChange is called inside DatepickerRange's internal reducer where state changes occur. This is problematic because the state changes may not have settled, and reducers must be pure and side effect free. The onChange function commonly contains side effects from the consumer of the component.

We will continue to investigate the problem and update this issue with progress.

hzhu commented

Thanks @pedropmedina! The issue has been fixed in #1309.