Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't figure out how to use this with Formik field component #298

Closed
Shaker-Hamdi opened this issue Nov 20, 2019 · 26 comments
Closed

Can't figure out how to use this with Formik field component #298

Shaker-Hamdi opened this issue Nov 20, 2019 · 26 comments

Comments

@Shaker-Hamdi
Copy link

I'm using Formik and trying to use this package, but Formik can't see the value of the input and thus doesn't validate or submit the form.

Here's the PhonInputField component that I'm using ...

import PhoneInput from "react-phone-number-input";

const PhoneInputField = ({
    field,
    form
}) => {
    return (
        <div className="input-field">
            <PhoneInput
                placeholder="Enter phone number"
                name={field.name}
                value={field.value}
                onChange={field.onChange}
                onBlur={field.onBlur}
            />
        </div>
    );
};

export default PhoneInputField;

And here's how I'm using it in Formik ...

<Formik
    initialValues={initialValues}
    validationSchema={signInSchema}
    onSubmit={handleFormSubmit}
>
    {({
        isValid,
        isSubmitting,
        handleChange,
        handleBlur
    }) => (
        <Form>


                <Field
                    type="tel"
                    name="phone_number"
                    component={PhoneInputField}
                />

            <div className="cta">
                <button
                    type="submit"
                    disabled={!isValid || isSubmitting}
                    className="btn btn-primary big"
                >Submit</button>
            </div>
        </Form>
    )}
</Formik>

What am I doing wrong here?

@catamphetamine
Copy link
Owner

Perhaps the reason could be formik expecting onChange(event) instead of onChange(value).
See if something like onChange={value => field.onChange({ target: { value } })} would work.

@Shaker-Hamdi
Copy link
Author

Shaker-Hamdi commented Nov 20, 2019

It actually now gives me this warning in the console ...

image

He's confused on which value to update.
And as you can see I did provide a "name" and the name shows in the inspector!

@catamphetamine
Copy link
Owner

Well, then it requires a properly constructed event it seems.
Somehow it can't figure out that field.onChange is for field.
You can experiment with constructing a synthetic React event of some sort.
This is an advanced topic I guess.

@catamphetamine
Copy link
Owner

Or maybe not, maybe it's something else.
I didn't use formik.

@Shaker-Hamdi
Copy link
Author

When I do it like this the warnings go away, but still can't seem to detect that the field has a value and thus won't activate the "submit" button ...

<PhoneInput
        placeholder="Enter phone number"
        name={field.name}
        value={field.value}
        onChange={value => field.onChange({ value })}
        onBlur={value => field.onBlur({ value })}
/>

@catamphetamine
Copy link
Owner

You'll have to ask formik experts then.

@Shaker-Hamdi
Copy link
Author

OK, thank you for your effort, I'll try to post this on StackOverflow, and maybe someone who faced this before can give his insight.

@catamphetamine
Copy link
Owner

There has also been a previous thread on Formik:
#159 (comment)
Maybe you could find something there.

@AndrewStratigos
Copy link

AndrewStratigos commented Nov 23, 2019

If it helps, here is my phoneinput/formik control

import React, { useState } from 'react';
import PhoneInput from 'react-phone-number-input';
import PropTypes from 'prop-types';
import 'react-phone-number-input/style.css';
import { getIn } from 'formik';


const PhoneInputField = (props) => {
  const {
    className,
    field: { name, value },
    form: {
      errors, handleBlur, setFieldValue, touched,
    },
    form,
    label,
    country,
    onChange,
    disabled,
  } = props;

  const [isFocused, setFocused] = useState(false);
  const isError = getIn(touched, name) && getIn(errors, name);
  const errorStyle = isError ? 'error' : '';
  const filledStyle = (isFocused || value) ? 'filled' : '';
  const disabledStyle = disabled ? 'disabled' : '';

  const handleInputBlur = (e) => {
    setFocused(false);
    handleBlur(e);
  };

  const handleInputFocus = () => setFocused(true);

  const onValueChange = (phoneNumber) => {
    setFieldValue(name, phoneNumber);

    if (onChange !== null) {
      onChange(phoneNumber);
    }
  };

  return (
    <div className={`${className} ${errorStyle} ${filledStyle} ${disabledStyle} text-input-group`}>
      <PhoneInput
        placeholder="Enter phone number"
        name={name}
        value={value}
        onChange={onValueChange}
        country={country}
      />
      <label className="transition ml-10" htmlFor={name}>
        {label}
      </label>
      <div className="flex h-5 items-end text-red-100 text-xs">
        {isError && getIn(errors, name)}
      </div>
    </div>
  );
};

PhoneInputField.propTypes = {
  className: PropTypes.string,
  form: PropTypes.any.isRequired,
  field: PropTypes.any.isRequired,
  onChange: PropTypes.func,
  label: PropTypes.string,
  country: PropTypes.string,
  disabled: PropTypes.bool,
};

PhoneInputField.defaultProps = {
  className: '',
  label: '',
  onChange: null,
  country: 'AU',
  disabled: false,
};

export default PhoneInputField;

@Shaker-Hamdi
Copy link
Author

@AndrewStratigos That did actually work, Thanks a lot man.
The trick in your code was this part ...

const onValueChange = phoneNumber => {
        setFieldValue(name, phoneNumber);

        if (onChange !== null) {
            onChange(phoneNumber);
        }
    };

Again, thanks a bunch 👍

@cortehz
Copy link

cortehz commented Nov 28, 2019

@Shaker-Hamdi. I'm still struggling to make this work for me. Can you take a look at my sandbox and see?

@jdmg94
Copy link

jdmg94 commented Jan 8, 2020

I may be late to the party but for anyone else here having issues with their onChange handler, remember this also comes with a curried option, in a regular web input formik can take the name attribute from the target it receives in the change handler, React-native generally doesn't provide a synthetic event (it does but its different) most people tend to use the onChangeText property instead so formik needs other queues to know how to update the form's state, consider the following:

instead of calling the change (or blur) handlers like we do on web: onChange={formik.handleChange}

you call it with the attribute name: onChange={formik.handleChange('phoneNumber')} this also works for the blur handler onBlur={formik.handleBlur('phoneNumber')} also make sure you send the change values as string or formik won't accept it, it sometimes is a pain in the ass to work forms on RN

@catamphetamine
Copy link
Owner

@jdmg94 So what does the ultimate Formik component look like then?
Where does formik.handleChange go in this code?

import React, { useCallback } from 'react'
import PropTypes from 'prop-types'
import PhoneInput from 'react-phone-number-input`

function FormikPhoneInput(ref, { name, onChange, formik, ...rest }) {
  const onChange_ = useCallback(value => onChange(value || ''), [onChange])
  return (
    <PhoneInput
      {...rest}
      ref={ref}
      name={name}
      onChange={onChange_}/>
  )
}

FormikPhoneInput = React.forwardRef(FormikPhoneInput)

FormikPhoneInput.propTypes = {
  name: PropTypes.string.isRequired,
  onChange: PropTypes.func.isRequired,
  formik: PropTypes.shape({
    handleChange: PropTypes.func.isRequired,
    handleBlur: PropTypes.func.isRequired
  }).isRequired
}

export default FormikPhoneInput

Also, in v3.x of this library, onBlur no longer sets event.target.value, so I removed onBlur from this code.
If you think it's still required then you could add it.

@jdmg94
Copy link

jdmg94 commented Jan 9, 2020

@jdmg94 So what does the ultimate Formik component look like then?
Where does formik.handleChange go in this code?

import React, { useCallback } from 'react'
import PropTypes from 'prop-types'
import PhoneInput from 'react-phone-number-input`

function FormikPhoneInput(ref, { name, onChange, formik, ...rest }) {
  const onChange_ = useCallback(value => onChange(value || ''), [onChange])
  return (
    <PhoneInput
      {...rest}
      ref={ref}
      name={name}
      onChange={onChange_}/>
  )
}

FormikPhoneInput = React.forwardRef(FormikPhoneInput)

FormikPhoneInput.propTypes = {
  name: PropTypes.string.isRequired,
  onChange: PropTypes.func.isRequired,
  formik: PropTypes.shape({
    handleChange: PropTypes.func.isRequired,
    handleBlur: PropTypes.func.isRequired
  }).isRequired
}

export default FormikPhoneInput

Also, in v3.x of this library, onBlur no longer sets event.target.value, so I removed onBlur from this code.
If you think it's still required then you could add it.

remove onChange_ and just call in your jsx onChange={formik.handleChange(name)} same with onBlur if you need validation on blur

@catamphetamine
Copy link
Owner

@jdmg94 Ok.
So, Formik users, if @jdmg94 's solution works for you then post your code.

@shirazz
Copy link

shirazz commented Feb 8, 2020

@catamphetamine please find a working implementation for future reference.
https://codesandbox.io/s/formik-react-phone-number-input-p5jvs

@boriswinner
Copy link

boriswinner commented Sep 26, 2020

I have modified @AndrewStratigos 's version. This version works right with Formik's onBlur, allowing you to validate the field on blur.

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,
};

@catamphetamine
Copy link
Owner

@boriswinner Ok, what are the main issues when using this library with formik "out of the box"?

  • Issue 1
  • Issue 2
    ...

Perhaps we could add a section in the readme on using this library with formik or other libraries.

@catamphetamine
Copy link
Owner

@boriswinner

Formik emits a name attribute, which has to be passed to the element. When trying to use your component, Formik logs:

name is passed through so it's not a issue of this library

The right solution will be to add a 'name' prop to the component which will correspond to the name attribute in HTML element.

That's how it currently is.

onChange emits the event as the third parameter. We need to write a wrapper that calls Formik's handleChange with the event as the parameter.

What? Not clear what you meant there.

@MidhaTahir
Copy link

In case if anyone needs help this is my current implementation. Below approach works with both onChange and onBlur events and doesn't throw error when string is given in field (disabled). Thanks to above replies:

import "react-phone-number-input/style.css";
import PhoneInput from "react-phone-number-input";

const initialValues = {
    phoneNumber: "",
};

const validate = (values) => {
    let errors = {};
    if (!values.phoneNumber) {
      errors.phoneNumber = "⋆Required";
    }
    return errors;
  };

const onSubmit = (values, onSubmitProps) => {....}

  const formik = useFormik({
    initialValues,
    onSubmit,
    validate,
  });

<PhoneInput
          className='anonymous'
          placeholder='Phone Number'
          name='phoneNumber'
          value={formik.values.phoneNumber}
          onChange={e => formik.setFieldValue("phoneNumber", e)}
          onBlur={formik.handleBlur("phoneNumber")}
        />
        {formik.touched.phoneNumber && formik.errors.phoneNumber ? (
          <div
            className='text-danger text-right'
            style={{ marginBottom: "-13px", fontSize: "12px" }}
          >
            {formik.errors.phoneNumber}
          </div>
        ) : null}

@ackalhan
Copy link

ackalhan commented Jun 5, 2021

This works fine for me, "react-hook-form": "^7.7.0"

<Controller
    name="contact_number"
    control={control}
    rules={{ required: true }}
    render={({ field }) => (
      <PhoneInput
        {...field}
        country="us"
      />
    )}
/>

@catamphetamine
Copy link
Owner

@ackalhan I've updated the README with a react-hook-form section.
https://github.com/catamphetamine/react-phone-number-input#react-hook-form

@jagnani73
Copy link

@jdmg94 So what does the ultimate Formik component look like then? Where does formik.handleChange go in this code?

import React, { useCallback } from 'react'
import PropTypes from 'prop-types'
import PhoneInput from 'react-phone-number-input`

function FormikPhoneInput(ref, { name, onChange, formik, ...rest }) {
  const onChange_ = useCallback(value => onChange(value || ''), [onChange])
  return (
    <PhoneInput
      {...rest}
      ref={ref}
      name={name}
      onChange={onChange_}/>
  )
}

FormikPhoneInput = React.forwardRef(FormikPhoneInput)

FormikPhoneInput.propTypes = {
  name: PropTypes.string.isRequired,
  onChange: PropTypes.func.isRequired,
  formik: PropTypes.shape({
    handleChange: PropTypes.func.isRequired,
    handleBlur: PropTypes.func.isRequired
  }).isRequired
}

export default FormikPhoneInput

Also, in v3.x of this library, onBlur no longer sets event.target.value, so I removed onBlur from this code. If you think it's still required then you could add it.

What do we need to pass in onChange prop?

@jagnani73
Copy link

If it helps, here is my phoneinput/formik control

import React, { useState } from 'react';
import PhoneInput from 'react-phone-number-input';
import PropTypes from 'prop-types';
import 'react-phone-number-input/style.css';
import { getIn } from 'formik';


const PhoneInputField = (props) => {
  const {
    className,
    field: { name, value },
    form: {
      errors, handleBlur, setFieldValue, touched,
    },
    form,
    label,
    country,
    onChange,
    disabled,
  } = props;

  const [isFocused, setFocused] = useState(false);
  const isError = getIn(touched, name) && getIn(errors, name);
  const errorStyle = isError ? 'error' : '';
  const filledStyle = (isFocused || value) ? 'filled' : '';
  const disabledStyle = disabled ? 'disabled' : '';

  const handleInputBlur = (e) => {
    setFocused(false);
    handleBlur(e);
  };

  const handleInputFocus = () => setFocused(true);

  const onValueChange = (phoneNumber) => {
    setFieldValue(name, phoneNumber);

    if (onChange !== null) {
      onChange(phoneNumber);
    }
  };

  return (
    <div className={`${className} ${errorStyle} ${filledStyle} ${disabledStyle} text-input-group`}>
      <PhoneInput
        placeholder="Enter phone number"
        name={name}
        value={value}
        onChange={onValueChange}
        country={country}
      />
      <label className="transition ml-10" htmlFor={name}>
        {label}
      </label>
      <div className="flex h-5 items-end text-red-100 text-xs">
        {isError && getIn(errors, name)}
      </div>
    </div>
  );
};

PhoneInputField.propTypes = {
  className: PropTypes.string,
  form: PropTypes.any.isRequired,
  field: PropTypes.any.isRequired,
  onChange: PropTypes.func,
  label: PropTypes.string,
  country: PropTypes.string,
  disabled: PropTypes.bool,
};

PhoneInputField.defaultProps = {
  className: '',
  label: '',
  onChange: null,
  country: 'AU',
  disabled: false,
};

export default PhoneInputField;

What do we need to pass in the onChange prop?

@gift56
Copy link

gift56 commented Nov 22, 2022

Thanks a lot MidhaTahir Now I have finally passed the issue from react-phone-input-2😊😊

@EOEboh
Copy link

EOEboh commented Feb 22, 2023

In case if anyone needs help this is my current implementation. Below approach works with both onChange and onBlur events and doesn't throw error when string is given in field (disabled). Thanks to above replies:

import "react-phone-number-input/style.css";
import PhoneInput from "react-phone-number-input";

const initialValues = {
    phoneNumber: "",
};

const validate = (values) => {
    let errors = {};
    if (!values.phoneNumber) {
      errors.phoneNumber = "⋆Required";
    }
    return errors;
  };

const onSubmit = (values, onSubmitProps) => {....}

  const formik = useFormik({
    initialValues,
    onSubmit,
    validate,
  });

<PhoneInput
          className='anonymous'
          placeholder='Phone Number'
          name='phoneNumber'
          value={formik.values.phoneNumber}
          onChange={e => formik.setFieldValue("phoneNumber", e)}
          onBlur={formik.handleBlur("phoneNumber")}
        />
        {formik.touched.phoneNumber && formik.errors.phoneNumber ? (
          <div
            className='text-danger text-right'
            style={{ marginBottom: "-13px", fontSize: "12px" }}
          >
            {formik.errors.phoneNumber}
          </div>
        ) : null}

Thank you so much. This helped me. Apparently, the trick was the formik.setFieldValue method

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests