IonInput and input masking in React

Hello,

I’ve been trying to implement a phone number mask on IonInput. imask and each third party library I’ve tried requires the native element as a reference (IonInput as the reference element will not work), so I’ve been trying this:

const MaskedStyledInput = IMaskMixin(({ inputRef, ...props }) => {
	return (
		<IonInput
			{...props}
			ref={async el => {
				const element = el && (await el.getInputElement())
				return inputRef(element)
			}}
		/>
	)
})

This doesn’t seem to work as the element isn’t available until after the first render has already happened, thus passing an undefined element to imask. It works perfectly with <input />.

Is there a recommended way of doing input masking in ionic/react outside of third party libraries? Thanks in advance for any help.

1 Like

I’m running into the same issue. Did you ever solve for this @austin43?

I did not. My app’s requirements shifted and no longer need it. It seems to be a pretty difficult problem to solve because of the way ion-input encapsulates the native input.

Yes the encapsulation really makes it challenging to use with the existing masking libraries. Libraries like IMask and react-input-mask need to attach refs to the native input element, but ion-input does not expose any way to attach a ref to the native input. And even if it did, the libraries would hook into the input’s handlers themselves & subvert ionic’s wrappers which I’m sure would just be a mess. After several hours of tinkering, I’ve realized the only reliable solution is going to be to roll my own (or to have Ionic themselves add the feature).

I’ll follow up on this thread once I have something that works.

Just following up to see if you had determined something that would work. Seems like because of what you described, using regex/masking or even any manipulation of native input elements becomes a challenge.

Still nothing. There’s not many apps that don’t use input masking in some capacity. Very strange to me that this isn’t addressed anywhere by the ionic docs.

Hey all,

I think I was able to get this working by using imask directly. This seems to work for me, can anyone confirm?

const MaskedIonInput = ({ value, onChange }: Props) => {
  const maskRef = useRef<IMask.InputMask<any> | null>(null);

  const inputCallback = useCallback(async (input) => {
    if (!input) {
      return;
    }
    const nativeInput = await input.getInputElement();
    const mask = IMask(nativeInput, {
      mask: Number,
      thousandsSeparator: ",",
    }).on('accept', (e: any) => {
      onChange(mask.value);
    }).on('complete', (e: any) => {
      onChange(mask.value);
    });

    maskRef.current = mask;
  }, []);

  return (
    <IonInput value={value} ref={inputCallback} />
  )
}

Usage would be:

const MyComponent: React.FC = () => {
  const [myValue, setMyValue] = useState('');

  return (
    <MaskedIonInput value={myValue} onChange={(v) => setMyValue(v)} />
   );
}

Probably would want to clean up the ref with a useEffect cleanup or something.

3 Likes

I am currently using Ionic 6 and IMask 6 and could not make this work, I get the following error when trying to create the mask:

Uncaught (in promise) TypeError: Cannot read properties of undefined (reading 'isContentEditable')
    at new InputMask (input.js:42:1)
    at IMask (holder.js:11:1)
    at index.tsx:42:1