bl00mber / react-phone-input-2

:telephone_receiver: Highly customizable phone input component with auto formatting
https://bl00mber.github.io/react-phone-input-2.html
MIT License
948 stars 536 forks source link

Use the component with Formik #323

Open boriswinner opened 3 years ago

boriswinner commented 3 years ago

There are two issues when trying to use the component with Formik:

Here's my React component that 'solves' these issues:

import React, { useEffect } from 'react';
import PropTypes from 'prop-types';
import PhoneInput from 'react-phone-input-2';
import block from 'bem-css-modules';
import classNames from 'classnames';
import { createRandomIdentificator } from '../../../helpers/commonHelpers';
import styles from './styles.module.scss';

const b = block(styles);

export default function MaskedInput(props) {
    const {
        handleChange,
        handleBlur,
        className,
        type,
        placeholder,
        name,
        ...restProps
    } = props;

    const inputClassPostfix = createRandomIdentificator();

    useEffect(() => {
        document.querySelector(`.phone-input-${inputClassPostfix}`).setAttribute('name', name);
    });

    const onValueChange = (phoneNumber, country, e) => {
        handleChange(e);
    };

    return (
        <PhoneInput
            {...restProps}
            country="ru"
            preferredCountries={['ru']}
            value={null}
            placeholder={placeholder}
            containerClass={b('container')}
            inputClass={classNames(b('input'), `phone-input-${inputClassPostfix}`)}
            buttonClass={b('button')}
            onChange={onValueChange}
            onBlur={handleBlur}
            name={name}
        />
    );
}

MaskedInput.defaultProps = {
    type: 'text',
    placeholder: '',
    name: '',
    className: '',
};

MaskedInput.propTypes = {
    name: PropTypes.string,
    placeholder: PropTypes.string,
    type: PropTypes.string,
    className: PropTypes.string,
    handleBlur: PropTypes.func.isRequired,
    handleChange: PropTypes.func.isRequired,
};
BrainBuzzer commented 3 years ago

Facing a similar problem

Dono7 commented 3 years ago

I am also facing the same problem here. I think that it could be nice to have an optional name props Thank you @boriswinner for the hack!

smythluke commented 3 years ago

For the name issue I have used the inputProps option and I've just taken your code to solve the handleChange issue too: <PhoneInput ... inputProps={{name:"phoneNumber"}} onChange={(phoneNumber, country, e)=>{handleChange(e)}} /> Seems to work for me without a wrapper class!

mindscratch commented 2 years ago

I ended up with:

<Formik>
{({ errors, touched, values, handleChange, handleBlur }) => (
    <Form>
    <label htmlFor="phone">Phone Number</label>
    <PhoneInput
        inputProps={{name:"phone"}} 
        onChange={(phoneNumber, country, e)=>{handleChange(e)}}
        onBlur={handleBlur}
    />
    </Form>
)}
</Formik>
Bryan-Herrera-DEV commented 1 year ago

{ handleChange, handleBlur, className, type, placeholder, name, ...restProps }

Thx men, simple and easy solution, it worked for me.

badalsaibo commented 1 year ago

 const validationSchema = Yup.object().shape(
    {
      phone: Yup.string()
        .nullable()
        .notRequired()
        .when(['phone'], (value, schema) => {
          if (Number(value) > Number(phoneCode)) {
            return schema
              .required()
              .min(MIN_PHONE_NUMBER_LENGTH_WITH_PHONE_CODE, 'Should not be less than 8 digits')
              .max(MAX_PHONE_NUMBER_LENGTH_WITH_PHONE_CODE, 'Should not exceed 13 digits');
          }
          return schema;
        }),
    },
    [['phone', 'phone']]
  );

  const formik = useFormik({
    initialValues: {
      phone: '',
    },
    validationSchema,
    validateOnChange: true,
  });

 const { errors, handleBlur, isValid, values } = formik;

 const onValueChange = (phoneNumber) => {
    formik.setFieldValue('phone', phoneNumber);
  };

 return (
     <StyledPhoneInput
          inputProps={{
            name: 'phone', // should match with the schema and initialValues
          }}
          disableDropdown
          onBlur={handleBlur} // 👈
          onChange={onValueChange} // 👈
          countryCodeEditable={false}
          preferredCountries={['gb', 'us']}
          country={userCountryCode?.toLowerCase() || 'gb'}
          containerClass={`react-phone-number ${errors.phone ? 'error' : ''}`}
          onlyCountries={countriesList.map((o) => o.countryCode.toLowerCase())}
      />
  );

Styled with error for css material ui

const StyledPhoneInput = styled(PhoneInput)(({ theme }) => ({
  '&.react-phone-number': {
    fontFamily: 'inherit',
  },
  '&.react-phone-number input.form-control': {
    fontFamily: 'inherit',
    width: '100%',
    paddingTop: theme.spacing(1),
    paddingBottom: theme.spacing(1),
    borderRadius: '8px',
    borderColor: 'rgba(145, 158, 171, 0.32)',
    paddingLeft: theme.spacing(2.5),
    '&:focus': {
      borderColor: theme.palette.primary.main,
      boxShadow: `0 0 0 1px ${theme.palette.primary.main}`,
    },
  },
  '&.react-phone-number.error': {
    '& input': {
      borderColor: theme.palette.error.main,

      '&:focus': {
        borderColor: theme.palette.error.main,
        boxShadow: `0 0 0 1px ${theme.palette.error.main}`,
      },
    },

    '& .special-label': {
      color: theme.palette.error.main,
    },
  },
}));