Backspace key writes "Ba" or "BB" in the Input when used with Mask "99.999.999-[99]|[aa]"
Description:
When using the mask "99.999.999-[99]|[aa]" with the use-mask-input library, pressing the Backspace key results in the word "Backspace" being written into the input. However, since the mask allows only 2 characters for letters, it ends up writing "Ba" or "BB".
Code Example:
<Input
type="text"
label={
<span>
RG: <span className="text-red-500">*</span>
</span>
}
variant="bordered"
defaultValue={control._formValues["rg"]}
isInvalid={!!errors.rg}
labelPlacement="outside"
placeholder="xx.xxx.xxx-xx"
errorMessage={errors.rg?.message as string}
className="max-w-[40rem]"
{...registerWithMask("rg", "99.999.999-[99]|[aa]", {
required: true,
autoUnmask: true,
})}
/>
Steps to Reproduce:
Use the mask "99.999.999-[99]|[aa]" in an input field. Type a valid RG number. (In Brazil some states RG has letters at the end) Press the Backspace key.
Expected Behavior:
The Backspace key should delete the previous character, not write "Ba" or "BB" into the input field.
Actual Behavior:
Pressing the Backspace key writes "Ba" or "BB" into the input field.
Environment:
Library Version: 3.4.0 I'm using the mask with react-hook-form
This issue seems to occur specifically with masks that include optional letter characters. It prevents proper user experience by inserting unwanted characters when attempting to delete.
Thank you for looking into this issue.
for some reason when i use the hook. The Backspace and Delete are write instead erase the input
Same problem, here is kind of confusing.
Same
Same thing, any workarounds for this?
Horrible hack but it works...
useEffect(() => {
if (value && value.indexOf("BACKSPACE") > -1) {
setValue(value.replace("BACKSPACE", "_"));
}
}, [value]);
<input
ref={masked ? withMask(mask) :undefined}
value={value}
onChange={e => {
setValue(e.target.value);
}}
onFocus={() => {
setMasked(true);
}}
onBlur={() => {
setMasked(false);
}}
/>
This is a simple workaround that worked for me. You can implement more custom logic if needed in the handleBeforeWrite function
const InputMaskField = ({ field }: { field: Field }) => {
const {
register,
setValue,
formState: { errors },
} = useFormContext();
const registerWithMask = useHookFormMask(register);
const handleBeforeWrite = (event: KeyboardEvent) => {
if (event.key === 'Backspace' || event.key === 'Delete') {
setValue(field.key, '', {
shouldValidate: true,
shouldDirty: true,
});
return { rewritePosition: 0 };
}
return { rewritePosition: undefined };
};
return (
<div className="w-full flex flex-col gap-2">
<Input
id={field.key}
type="text"
placeholder={field?.placeholder || ''}
{...registerWithMask(field.key, field.mask || [], {
autoUnmask: true,
onBeforeWrite: handleBeforeWrite,
})}
/>
{errors[field.key]?.message && (
<FieldError>{String(errors[field.key]?.message)}</FieldError>
)}
</div>
);
};
Workarounds are kinda working but have disadvantages. The only reason I could not use the lib is the error shown above... Hope the author will quickly fix that.
Yet another partial workaround is to just define letters you expect to be typed. In my case just letter 'X'.
<Input
{...field}
ref={withMask("9999-999(9|X)", {
definitions: {
X: {
validator: "[xX]",
casing: "upper",
},
},
})}
/>
This is still ocurring as of today
Requires InputMask update https://github.com/RobinHerbots/Inputmask/commit/aba4c29d045daa349891c36bc49c458d3550fdc8
@RianReisBRQ I installed the next version of inputmask and created myself a similar class to the one in here (I dont use react hook form)
import Inputmask from 'inputmask';
type Options = Inputmask.Options;
type Input = HTMLInputElement | HTMLTextAreaElement | HTMLElement | HTMLInputElement | null;
export function withMask(mask: string, options?: Options): (input: Input) => void;
export function withMask(options: Options): (input: Input) => void;
export function withMask(): (input: Input) => void;
export function withMask(maskOrOptions?: string | Options | null, options?: Options): (input: Input) => void {
return (input: Input) => {
if (!(
typeof window !== 'undefined'
&& window.document
&& window.document.createElement
)) return;
let maskInput: Inputmask.Instance;
if (typeof maskOrOptions === 'string') {
maskInput = Inputmask(maskOrOptions, options);
} else if (maskOrOptions && typeof maskOrOptions === 'object') {
maskInput = Inputmask(maskOrOptions);
} else if (options) {
maskInput = Inputmask(options);
} else {
// Default empty mask if no parameters
maskInput = Inputmask();
}
if (input) {
maskInput.mask(input);
}
}
}
Only issue is that using regex still has the backspace issue so I replaced it with a mix of mask and definitions (like the suggestion from march).
That "BA" annoying bug can be even easily replicated in the very first manual example:
ref={withMask("AA99 AAA", {
placeholder: "_",
showMaskOnHover: false,
})}
You simply add that to an input, type anything, then backspace it and voila - a cursed "BA" appears