Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to integrate react-intl-tel-input with formik?

I'm using Formik to handle forms in my ReactJs app, I would like to use react-intl-tel-input to handle a phone number, however I'm unable to integrate the handleChange, handleBlur and validations with Formik. Right now I'm using my form's state to save the phone number and its validation status, but this generate a problem with Formik by re-render my other fields.

Here is my phone number component:

<IntlTelInput
  fieldId="userPhoneNumber"
  fieldName="userPhoneNumber"
  value={values.userPhoneNumber}
  preferredCountries={preferredMobileCountries}
  css={['intl-tel-input', `form-control ${(!validPhoneNumber) ? 'is-invalid' : ''}`]}
  style={{display: 'block',width: '100%'}}
  format
  onPhoneNumberChange={this.handlePhoneChange}
/>
{!validPhoneNumber && <div className="invalid-feedback">Invalid phone number</div>}

Which is the correct way to accomplish this? I mean use a custom component but be able to use the handleChange, handleBlur and validation schema of Formik?

Thanks in advance...

like image 702
lisdey89 Avatar asked Sep 03 '25 02:09

lisdey89


2 Answers

This is not the optimal solution, but links back IntlTelInput to formik's setFieldTouched and setFieldValue.

// @flow

import React, {Component, Fragment} from 'react';
import {ErrorMessage, Field} from 'formik';
import IntlTelInput from 'react-intl-tel-input';

export default class MobileField extends Component {
  formatPhoneNumberOutput(
    isValid: boolean,
    newNumber: string,
    countryData: Object,
    fullNumber: string,
    isExtension: boolean
  ) {
    if (isValid && fullNumber) {
      return fullNumber.replace(/(\s|-)/g, '');
    }
    return 'invalid_phone_number'; // caught by validator
  }

  render() {
    return (
      <Field
        name={name}
        render={({field, form: {errors, isSubmitting, touched, setFieldTouched, setFieldValue}}) => {
          return (
            <Fragment>
              <IntlTelInput
                defaultCountry="fr"
                defaultValue={field.value}
                disabled={isSubmitting}
                fieldId={name}
                fieldName={name}
                onPhoneNumberBlur={() => {
                  setFieldTouched(name, true);
                }}
                onPhoneNumberChange={(...args) => {
                  setFieldValue(name, this.formatPhoneNumberOutput(...args));
                }}
                preferredCountries={['fr', 'gb', 'es', 'be', 'de']}
              />
              <ErrorMessage name={name} render={msg => <p>{msg}</p>} />
            </Fragment>
          );
        }}
      />
    );
  }
}

use a validator such as validate.js to check that phone number is not "invalid_phone_number"

// @flow

import _mapValues from 'lodash/mapValues';
import validate from 'validate.js';

export type Values = {
    mobile: string,
    landline: string
};

export default (values: Values) => {
    const options = {
        fullMessages: false
    };
  const validation: {[key: string]: string[]} = validate(
      values,
      {
          mobile: {
              presence: {message: 'Please add a mobile phone number'},
              format: {
                  pattern: '^((?!invalid_phone_number).)*$', // is not invalid_phone_number
                  message: 'This phone number looks like being invalid'
              }
        },
        landline: {}
    },
    options
    );
    return _mapValues(validation, messages => messages[0]);
};
like image 182
gasp Avatar answered Sep 04 '25 20:09

gasp


In case anybody is looking to integrate into a functional component (rather than a class based component) this may save you some time! :)

import React, { useState } from 'react';
import PropTypes from 'prop-types';
import IntlTelInput from 'react-intl-tel-input';
import { Field } from 'formik';
import 'react-intl-tel-input/dist/main.css';

const TelephoneInput = ({ name, ...props }) => {

  const [telephoneValid, setTelephoneValid] = useState(true);
  const setValidity = valid => {
    setTelephoneValid(valid);
  };
  // process number into string with area code for submission
  const processNumber = (isValid, phone, country) => {
    return `+${country.dialCode} ${phone}`;
  };

  return (
    <>
      <Field name={name}>
        {(
          { field: { value },
          form: { isSubmitting, setFieldTouched, setFieldValue } }) =>
            <IntlTelInput
              {...props}
              containerClassName="intl-tel-input"
              inputClassName={telephoneValid ? 'valid' : 'invalid'}
              label="telephone"
              defaultValue={value}
              disabled={isSubmitting}
              fieldId={name}
              fieldName={name}
              onPhoneNumberBlur={(isValid) => {
                setFieldTouched(name, true);
                setValidity(isValid);
              }}
              onPhoneNumberChange={(isValid, phone, country) => {
                setFieldValue(name, processNumber(isValid, phone, country));
              }}
            />
        }
      </Field>
    </>
  );
};

TelephoneInput.propTypes = {
  name: PropTypes.string.isRequired,
};
export default TelephoneInput;
like image 42
Oli C Avatar answered Sep 04 '25 18:09

Oli C