React - How to format phone number as user types React - How to format phone number as user types reactjs reactjs

React - How to format phone number as user types


You can normalize the input like so

  • the value is up-to-date in relation to event.target.value
  • previousValue is what has already been validated and set to state

This is structured in a way to prevent invalid characters from updating the input and also limits the input to 10 numbers.

Click the button below for a working example.


const normalizeInput = (value, previousValue) => {  // return nothing if no value  if (!value) return value;   // only allows 0-9 inputs  const currentValue = value.replace(/[^\d]/g, '');  const cvLength = currentValue.length;   if (!previousValue || value.length > previousValue.length) {    // returns: "x", "xx", "xxx"    if (cvLength < 4) return currentValue;     // returns: "(xxx)", "(xxx) x", "(xxx) xx", "(xxx) xxx",    if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`;     // returns: "(xxx) xxx-", (xxx) xxx-x", "(xxx) xxx-xx", "(xxx) xxx-xxx", "(xxx) xxx-xxxx"    return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`;   }};

const normalizeInput = (value, previousValue) => {  if (!value) return value;  const currentValue = value.replace(/[^\d]/g, '');  const cvLength = currentValue.length;    if (!previousValue || value.length > previousValue.length) {    if (cvLength < 4) return currentValue;    if (cvLength < 7) return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3)}`;    return `(${currentValue.slice(0, 3)}) ${currentValue.slice(3, 6)}-${currentValue.slice(6, 10)}`;  }};const validateInput = value => {  let error = ""    if (!value) error = "Required!"  else if (value.length !== 14) error = "Invalid phone format. ex: (555) 555-5555";    return error;};    class Form extends React.Component {  constructor() {    super();        this.state = { phone: "", error: "" };    this.handleChange = this.handleChange.bind(this);    this.handleSubmit = this.handleSubmit.bind(this);    this.handleReset = this.handleReset.bind(this);  }    handleChange({ target: { value } }) {       this.setState(prevState=> ({ phone: normalizeInput(value, prevState.phone) }));  };    handleSubmit(e) {    e.preventDefault();    const error = validateInput(this.state.phone);        this.setState({ error }, () => {       if(!error) {         setTimeout(() => {           alert(JSON.stringify(this.state, null, 4));         }, 300)       }    });  }    handleReset() {     this.setState({ phone: "", error: "" });  };    render() {    return(      <form className="form" onSubmit={this.handleSubmit}>        <div className="input-container">          <p className="label">Phone:</p>          <input            className="input"            type="text"            name="phone"            placeholder="(xxx) xxx-xxxx"            value={this.state.phone}            onChange={this.handleChange}          />          {this.state.error && <p className="error">{this.state.error}</p>}        </div>        <div className="btn-container">          <button              className="btn danger"             type="button"             onClick={this.handleReset}           >            Reset          </button>          <button className="btn primary" type="submit">Submit</button>        </div>      </form>    );  }}ReactDOM.render(  <Form />,  document.getElementById('root'));
html {  font-family: -apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,"Noto Sans",sans-serif,"Apple Color Emoji","Segoe UI Emoji","Segoe UI Symbol","Noto Color Emoji";  font-size: 16px;  font-weight: 400;  line-height: 1.5;  -webkit-text-size-adjust: 100%;  background: #fff;  color: #666;}.btn {  color: #fff;  border: 1px solid transparent;  margin: 0 10px;  cursor: pointer;  text-align: center;  box-sizing: border-box;  padding: 0 30px;  vertical-align: middle;  font-size: .875rem;  line-height: 38px;  text-align: center;  text-decoration: none;  text-transform: uppercase;  transition: .1s ease-in-out;  transition-property: color,background-color,border-color;}.btn:focus {  outline: 0;}.btn-container {  text-align: center;  margin-top: 10px;}.form {  width: 550px;  margin: 0 auto;}.danger {  background-color: #f0506e;  color: #fff;  border: 1px solid transparent;} .danger:hover {  background-color: #ee395b;  color: #fff;}.error {  margin: 0;  margin-top: -20px;  padding-left: 26%;  color: red;  text-align: left;}.input {  display: inline-block;  height: 40px;  font-size: 16px;  width: 70%;  padding: 0 10px;  background: #fff;  color: #666;  border: 1px solid #e5e5e5;  transition: .2s ease-in-out;  transition-property: color,background-color,border; }.input-container {  width: 100%;  height: 60px;  margin-bottom: 20px;  display: inline-block;}.label {  width: 25%;  padding-top: 8px;  display: inline-block;  text-align: center;  text-transform: uppercase;  font-weight: bold;  height: 34px;  border-top-left-radius: 4px;  border-bottom-left-radius: 4px;  background: rgb(238, 238, 238);}.primary {  background-color: #1e87f0;}.primary:hover {  background-color: #0f7ae5;  color: #fff;}
<script src="https://cdnjs.cloudflare.com/ajax/libs/react/16.6.3/umd/react.production.min.js"></script><script src="https://cdnjs.cloudflare.com/ajax/libs/react-dom/16.6.3/umd/react-dom.production.min.js"></script><div id='root'></div>