diff --git a/src/components/RelativeDateField/__tests__/RelativeDateField.test.tsx b/src/components/RelativeDateField/__tests__/RelativeDateField.test.tsx index 7adabd5..a172e64 100644 --- a/src/components/RelativeDateField/__tests__/RelativeDateField.test.tsx +++ b/src/components/RelativeDateField/__tests__/RelativeDateField.test.tsx @@ -127,7 +127,9 @@ describe('RelativeDateField', () => { await userEvent.click(screen.container); - expect(input).toHaveValue('now - 1d'); + // Blur is dispatched asynchronously (useFocusWithin defers it), so wait + // for the committed value to be restored before asserting. + await expect.poll(() => input.value).toBe('now - 1d'); expect(hiddenInput).toHaveValue('now - 1d'); expect(onUpdate).not.toHaveBeenCalled(); }); diff --git a/src/components/RelativeDateField/hooks/useRelativeDateFieldState.ts b/src/components/RelativeDateField/hooks/useRelativeDateFieldState.ts index f215639..72e3d01 100644 --- a/src/components/RelativeDateField/hooks/useRelativeDateFieldState.ts +++ b/src/components/RelativeDateField/hooks/useRelativeDateFieldState.ts @@ -86,20 +86,28 @@ export function useRelativeDateFieldState(props: RelativeDateFieldOptions): Rela [props.disabled, props.readOnly, setValue], ); + // Keep the latest state in a ref so `confirmValue` (invoked from a blur that + // may fire after an external value update) always reads current values + // instead of a stale render closure. + const latest = React.useRef({text, value, parsedDate}); + latest.current = {text, value, parsedDate}; + const confirmValue = React.useCallback(() => { - if (!text) { + const {text: currentText, value: currentValue, parsedDate: currentParsed} = latest.current; + + if (!currentText) { return; } - if (parsedDate) { - commitValue(text); + if (currentParsed) { + commitValue(currentText); return; } - const newValue = parseRelativeDate(value) ? value : null; + const newValue = parseRelativeDate(currentValue) ? currentValue : null; setText(newValue ?? ''); commitValue(newValue); - }, [commitValue, parseRelativeDate, parsedDate, text, value]); + }, [commitValue, parseRelativeDate]); const handleTextChange = React.useCallback( (t: string) => {