I have a react-hook-form
that takes in user data. For the user's cellPhoneNumber, I am using react-phone-number-input
library's PhoneInputWithCountry
.
For my form, I have a 'Save Changes' button that becomes active or inactive based on the details entered.
.
.
.
<EuiFlexItem grow={false}>
<EuiButton
fill
form={formId}
isDisabled={!methods.formState.isDirty || userQuery.isError}
isLoading={isFetchingOrPatchingUser}
type="submit"
>
Save changes
</EuiButton>
</EuiFlexItem>
.
.
.
Following is the controller for my 'CellPhoneNumber' field:
<Controller
name="cellPhoneNumber"
render={({
field: { ref, ...fieldProps },
fieldState: { invalid, error },
}) => (
<EuiFormHookRow
label="Cell Phone Number"
error={error}
isInvalid={invalid}
fullWidth
>
<PhoneInputWithCountry
inputComponent={EuiCellPhoneNumberFieldText}
inputRef={ref}
isInvalid={invalid}
placeholder="Enter phone number"
disabled={isDisabled}
defaultCountry="US"
international
{...fieldProps}
onBlur={() => {
console.log('Blur Called', fieldProps);
fieldProps.onBlur();
trigger(['emailAddress']);
}}
/>
</EuiFormHookRow>
)}
/>
Code for custom input:
const EuiCellPhoneNumberFieldText = forwardRef<
HTMLInputElement,
EuiFieldTextProps
>(function EuiCellPhoneNumberInput(props, ref) {
console.log(props.onFocus);
return <EuiFieldText {...props} disabled={isDisabled} inputRef={ref} />;
});
Now, the PhoneInput
component in itself works as intended, allowing me to udpate the cellPhoneNumber. But when I try and use the 'inputComponent' prop and pass in a custom input field using forwardRef, every time the form is re-rendered (mostly triggered by 'Save Changes' button being re-rendered) the custom input field goes out of focus. This does not happen when using the default input that comes with PhoneInput
but when passing a custom input component, this behaviour is observed.
So the question is, why is this happening and is there a way to bring the custom input field back in focus.
I suspect this might be something related to forwarding refs or props. Feel free to follow-up any additional information you might need.
Thank you for the help.
References:
I tried changing the props and manually writing the onChange
and onBlur
methods but both of them didn't work. The code is still not behaving as intended when using customInput.
As identified via the comments, the issue is this: EuiCellPhoneNumberFieldText
was being defined inside another component. Something like this:
const OuterComponent = (props) => {
const EuiCellPhoneNumberFieldText = forwardRef<
HTMLInputElement,
EuiFieldTextProps
>(function EuiCellPhoneNumberInput(props, ref) {
console.log(props.onFocus);
return <EuiFieldText {...props} disabled={isDisabled} inputRef={ref} />;
});
// ...
<PhoneInputWithCountry
inputComponent={EuiCellPhoneNumberFieldText}
// ...
/>
};
The problem with this is that every time the outer component renders, it makes a brand new definition for EuiCellPhoneNumberFieldText. It may have the same text as the last time, but it's a different function in memory. That means that when react compares the types of those two components, they will look like they are different component types. Thus, react is forced to unmount the old one and mount the new one. The new element does not have focus, so it looks like focus has been lost
The fix for this is to move EuiCellPhoneNumberFieldText outside, so it it just defined once:
const EuiCellPhoneNumberFieldText = forwardRef<
HTMLInputElement,
EuiFieldTextProps
>(function EuiCellPhoneNumberInput(props, ref) {
console.log(props.onFocus);
return <EuiFieldText {...props} disabled={isDisabled} inputRef={ref} />;
});
const OuterComponent = (props) => {
// ...
<PhoneInputWithCountry
inputComponent={EuiCellPhoneNumberFieldText}
// ...
/>
}