I have a React component WidgetInlineEditField that wraps an inline-editable input field. When editing a time value using a TimeInput component with a dropdown, the onBlur event fires before the onChange event, causing the selected value to be lost. Problem Description: The WidgetInlineEditField has an onBlur handler that checks if the focus moved outside the component container:
onBlur={(e): void => {
if (!e.currentTarget.contains(e.relatedTarget)) {
handleCancel(); // This cancels the edit and loses the value
}
}}
The TimeInput component renders its dropdown in a portal, so when a user clicks on a dropdown option:
- onBlur fires on the container (because relatedTarget is outside the container)
- handleCancel() is called immediately, canceling the edit
- onChange from the dropdown never gets a chance to update the value
// WidgetInlineEditField.tsx
export function WidgetInlineEditField({ onSave, onCancel, children }) {
const handleCancel = (): void => {
onCancel();
setEdit(false);
};
return (
{
if (!e.currentTarget.contains(e.relatedTarget)) {
handleCancel();
}
}}
>
{edit ? children : readValue}
);
}
// Usage in parent component
setValue(originalValue)}
>
Environment:
React 16.14.0
React-ui 3.1.3
TypeScript 4.0.3
What I've tried:
Using setImmediate: Doesn't help as the timing issue persists.
Using setTimeout with document.activeElement check
// WidgetInlineEditField.tsx export function WidgetInlineEditField({ onSave, onCancel, children }) { const handleCancel = (): void => { onCancel(); setEdit(false); }; return ({ if (!e.currentTarget.contains(e.relatedTarget)) { handleCancel(); } }} > {edit ? children : readValue}); } // Usage in parent componentsetValue(originalValue)} >
What I've tried**:**
- Using setImmediate: Doesn't help as the timing issue persists.
Using setTimeout with document.activeElement check:
onBlur={(e): void => {
setTimeout(() => {
if (!e.currentTarget.contains(document.activeElement)) {
handleCancel();
}
}, 0);
}}
This works for the dropdown selection case, but breaks existing tests in other components that use WidgetInlineEditField. The tests expect immediate cancellation on blur, but the timeout delays this behavior.
Moving the onBlur handler to the input element itself - doesn't work because focus moves to the dropdown
Checking if the dropdown is open - no API available in the TimeInput component
Expected behavior:
When selecting a value from the dropdown, the onChange should fire first, update the value, and then onBlur can handle the focus loss appropriately.