Create a phone mask on React.js w/o libraries


Photo by Lautaro Andreani on Unsplash

Recently I had to create a form that includes a phone number input. Such number input had to had some validations and an input mask to display properly the phone number.

Nowadays there are a different npm packages that might help with this task (like react-input-mask), but from times to times, you want to do it by yourself just because you want to know the insights of a specific feature and know how it works!

So, first, there is a must read Medium article where explains the functioning of onKeyDown and onInput props of an input field. So, I strongly recommend to read that article before you move on.

Now that you read that article (because you read it, right? Right?) it’s time to show my solution for this task. Let’s assume we have our code like this:

import React, { useState, ChangeEvent } from 'react';

export default function App() {
  const [phoneNumber, setPhoneNumber] = useState<string>('');

  const handlePhoneNumber = (event: ChangeEvent<HTMLInputElement>) => {
    setPhoneNumber(event.target.value);
  };

  return (
    <div>
      <h1>Hello FusionDev!</h1>
      <input type="tel" value={phoneNumber} onChange={handlePhoneNumber} />
    </div>
  );
}

There’s no rocket science here, we have a state to control the input’s value and have a function that is in charge to update that state, that’s it. Now, first, we need to create a function that returns the value of the input in a phone format (for this example our format will be like (333) 333-333). So, here is that function:

const phoneNumberFormatter = (value: string): string => {
    if (!value) return value;

    const phoneNumber = value.replace(/[^\d]/g, '');
    if (phoneNumber.length < 4) return phoneNumber;
    if (phoneNumber.length < 7) {
      return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3)}`;
    }

    return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(
      3,
      6
    )}-${phoneNumber.slice(6, 10)}`;
  };

Now, that little piece of code is that receives the value of the phone number (a string) and first, it checks if there’s any value, once that it checks, it removes all non-digits characters (the replace part) and starts to check the length of the phone number and formats it (using the javascript slice method) accordingly.

The last return statement would be the ideal phone number formatting, because it returns the string that we want. Now, let’s begin to put it all together. In order for this to work we need to set the event.target.value to the value returned from this function in our handlePhoneNumber function, so, will be something like this:

const handlePhoneNumber = (event: ChangeEvent<HTMLInputElement>) => {
    const formattedInputValue = phoneNumberFormatter(event.target.value);
    event.target.value = formattedInputValue;
    setPhoneNumber(event.target.value);
  };

Now, if you start to type in your input you’ll notice that the value will be formatted accordingly.

Conclusion

One of the main ideas of programming is to not re-invent the wheel. Because, if there is something already built, why built another? You’re more than free to use any libraries, packages as you want on your projects, the main purpose of this entry was to demonstrate a little bit about how an input mask works.

The next step…if you’re working with forms, maybe one of the requirements is to store the phone number in plain text (than means no special chars like () or hypens), so, would be a good idea to “unmask” the value of the phone number (HINT: you could use some regex 😉 ).

And here is the final code, just in case. Hope you liked this post and see you next time.

import React, { useState, ChangeEvent } from 'react';

export default function App() {
  const [phoneNumber, setPhoneNumber] = useState<string>('');

  const phoneNumberFormatter = (value: string): string => {
    if (!value) return value;

    const phoneNumber = value.replace(/[^\d]/g, '');
    if (phoneNumber.length < 4) return phoneNumber;
    if (phoneNumber.length < 7) {
      return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(3)}`;
    }

    return `(${phoneNumber.slice(0, 3)}) ${phoneNumber.slice(
      3,
      6
    )}-${phoneNumber.slice(6, 10)}`;
  };

  const handlePhoneNumber = (event: ChangeEvent<HTMLInputElement>) => {
    const formattedInputValue = phoneNumberFormatter(event.target.value);
    event.target.value = formattedInputValue;
    setPhoneNumber(event.target.value);
  };

  return (
    <div>
      <h1>Hello FusionDev!</h1>
      <input type="tel" value={phoneNumber} onChange={handlePhoneNumber} />
    </div>
  );
}
,