Как интегрировать response-intl-tel-input с formik?
Я использую Formik для обработки форм в своем приложении ReactJ, я хотел бы использовать response-intl-tel-input для обработки телефонного номера, однако я не могу интегрировать handleChange, handleBlur и проверки с Formik. Прямо сейчас я использую состояние моей формы, чтобы сохранить номер телефона и его статус проверки, но это создает проблему с Formik путем повторного рендеринга моих других полей.
Вот мой компонент номера телефона:
<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>}
Какой правильный способ сделать это? Я имею в виду использовать пользовательский компонент, но иметь возможность использовать handleChange, handleBlur и схему проверки Formik?
Заранее спасибо...
4 ответа
В случае, если кто-то хочет интегрироваться в функциональный компонент (а не в компонент на основе классов), это может сэкономить вам время!:)
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;
Это не оптимальное решение, но связывает обратно IntlTelInput с setFieldTouched и setFieldValue от formik.
используйте валидатор, например validate.js, чтобы убедиться, что номер телефона не является "invalid_phone_number"
// @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>
);
}}
/>
);
}
}
Если вы используете хук useFormik
import IntlTelInput from "react-intl-tel-input";
<IntlTelInput
inputClassName="tel-number"
fieldId="number"
fieldName="number"
onPhoneNumberChange={(
isValid,
value,
selectedCountryData,
fullNumber
) => {
formik.handleChange("number")(fullNumber);
}}
/>
Мне удалось сделать все, используяref
Вот мой код для вдохновения
"use client";
import React, { useState } from 'react'
import { Formik, FormikProps, Form, ErrorMessage, FormikErrors } from 'formik';
import './intl-tel-input-override.css';
import IntlTelInput from "react-intl-tel-input";
import 'react-intl-tel-input/dist/main.css';
interface FormValues {
phone: string;
}
function SendOTPForm({
csrfToken
}: {
csrfToken: string | undefined
}) {
const intlTelephoneInputRef = React.useRef<any>(null);
return (
<Formik
initialValues={{
phone: '',
}}
onSubmit={async (values) => {
console.log(values)
}
}
validate={values => {
const errors: FormikErrors<FormValues> = {};
if (!values.phone) {
errors.phone = 'Please enter your phone number';
}
if (intlTelephoneInputRef.current) {
const value = intlTelephoneInputRef.current.state.value;
const fullNumber = intlTelephoneInputRef.current.formatFullNumber(
value
)
const isValid = intlTelephoneInputRef.current.isValidNumber(fullNumber)
if (!isValid) {
errors.phone = 'Please enter a valid phone number'
}
}
return errors;
}
}
>
{
({ values, setFieldValue, setErrors, setTouched }: FormikProps<FormValues>) => (
<Form className="space-y-4">
<div>
<label htmlFor="phone" className="block mb-2 text-sm font-medium text-gray-900 dark:text-gray-300">
Continue using phone number {values.phone}
</label>
<IntlTelInput
fieldName="phone"
fieldId="phone"
inputClassName="bg-gray-50 border border-gray-300 text-gray-900 text-sm rounded-lg focus:ring-primary-500 focus:border-primary-500 block w-full p-2.5 dark:bg-gray-700 dark:border-gray-600 dark:placeholder-gray-400 dark:text-white"
placeholder="Enter your phone number to continue"
preferredCountries={[
"in",
"us"
]}
defaultCountry="in"
autoComplete="tel"
geoIpLookup={(callback) => {
fetch("https://ipapi.co/json")
.then(function (res) { return res.json(); })
.then(function (data) { callback(data.country_code); })
.catch(function () { callback("us"); });
}}
ref={intlTelephoneInputRef}
onPhoneNumberChange={(
_isValid,
value,
_selectedCountryData,
fullNumber
) => {
setFieldValue('phone', fullNumber)
// If length is less than 10 then do not set it as touched for
// sake of user experience
if (value.length < 10) {
return
}
setTouched({ phone: true }, true)
}}
/>
<ErrorMessage
name="phone"
component={"div"}
className="text-red-500 mt-2"
/>
</div>
<button type="submit" className="w-full text-white bg-primary-600 hover:bg-primary-700 focus:ring-4 focus:outline-none focus:ring-primary-300 font-medium rounded-lg text-sm px-5 py-2.5 text-center dark:bg-primary-600 dark:hover:bg-primary-700 dark:focus:ring-primary-800">
Request OTP
</button>
</Form>
)
}
</Formik>
)
}
export default SendOTPForm