1- import {useMemo, useState} from "react";
1+ import {useCallback, useEffect, useMemo, useRef , useState} from "react";
22import ReactPhoneInput from "react-phone-input-2";
33
4- import {ParsePhoneNumber, PhoneInputProps, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";
5-
6- import "./style5.css";
4+ import {CountryData, PhoneInputProps, PhoneNumber, ReactPhoneOnChange, ReactPhoneOnMount} from "../types";
75
6+ import styleInject from "./style";
87import masks from "./phoneMasks.json";
98import timezones from "./timezones.json";
109import validations from "./validations.json";
1110
11+ styleInject("style5.css");
12+
13+ let browserLoaded = true;
14+
1215type ISO2Code = keyof typeof masks;
1316type Timezone = keyof typeof timezones;
1417
@@ -18,7 +21,7 @@ const getDefaultISO2Code = () => {
1821 return (timezones[timezone] || "").toLowerCase() || "us";
1922}
2023
21- const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
24+ const parsePhoneNumber = (value: string , data: CountryData , formattedNumber: string): PhoneNumber => {
2225 const isoCode = data?.countryCode;
2326 const countryCodePattern = /\+\d+/;
2427 const areaCodePattern = /\((\d+)\)/;
@@ -42,14 +45,7 @@ const parsePhoneNumber: ParsePhoneNumber = (value, data, formattedNumber) => {
4245 const phoneNumberMatch = value ? (value.match(phoneNumberPattern) || []) : [];
4346 const phoneNumber = phoneNumberMatch.length > 1 ? phoneNumberMatch[1] : null;
4447
45- /** Checks if both the area code and phone number length satisfy the validation rules */
46- const rules = validations[isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
47- const valid = [
48- rules.areaCode.includes((areaCode || "").toString().length),
49- rules.phoneNumber.includes((phoneNumber || "").toString().length),
50- ].every(Boolean);
51-
52- return {countryCode, areaCode, phoneNumber, isoCode, valid, dialChanged};
48+ return {countryCode, areaCode, phoneNumber, isoCode, dialChanged};
5349}
5450
5551const PhoneInput = ({
@@ -64,6 +60,9 @@ const PhoneInput = ({
6460 inputClass: inputClassProxy,
6561 ...reactPhoneInputProps
6662 }: PhoneInputProps) => {
63+ const loaded = useRef(browserLoaded);
64+ const reset = useRef(false);
65+ const initialized = useRef(false);
6766 const [currentCode, setCurrentCode] = useState("");
6867
6968 const countryCode = useMemo(() => country || getDefaultISO2Code(), [country]);
@@ -79,29 +78,50 @@ const PhoneInput = ({
7978 return inputClassProxy ? `${className} ${inputClassProxy}` : className;
8079 }, [inputClassProxy, size]);
8180
82- const onChange: ReactPhoneOnChange = (value, data, event, formattedNumber) => {
81+ const checkValidity = (metadata: PhoneNumber) => {
82+ /** Checks if both the area code and phone number length satisfy the validation rules */
83+ const rules = validations[metadata.isoCode as ISO2Code] || {areaCode: [], phoneNumber: []};
84+ const isValid = reset.current || ((loaded.current || initialized.current) ? [
85+ rules.areaCode.includes((metadata.areaCode || "").toString().length),
86+ rules.phoneNumber.includes((metadata.phoneNumber || "").toString().length),
87+ ].every(Boolean) : !initialized.current);
88+ initialized.current = true;
89+ loaded.current = false;
90+ reset.current = false;
91+ return isValid;
92+ }
93+
94+ const onChange: ReactPhoneOnChange = useCallback((value, data, event, formattedNumber) => {
8395 const {dialChanged, ...metadata} = parsePhoneNumber(value, data, formattedNumber);
8496 const code = metadata.isoCode as ISO2Code;
8597
8698 if (code !== currentCode) {
8799 /** Clears phone number when the country is selected manually */
88100 metadata.areaCode = dialChanged ? null : metadata.areaCode;
89101 metadata.phoneNumber = null;
90- metadata.valid = false;
91102 setCurrentCode(code);
92103 }
93104
94- handleChange(metadata, event);
95- }
105+ handleChange({... metadata, valid: () => checkValidity(metadata)} , event);
106+ }, [currentCode, handleChange]);
96107
97- const onMount: ReactPhoneOnMount = (rawValue, {countryCode, ...event}, formattedNumber) => {
108+ const onMount: ReactPhoneOnMount = useCallback( (rawValue, {countryCode, ...event}, formattedNumber) => {
98109 const {dialChanged, ...metadata} = parsePhoneNumber(rawValue, {countryCode}, formattedNumber);
99- /** Initiates the current country code with the code of initial value */
100- setCurrentCode(metadata.isoCode as ISO2Code);
101110 /** Initializes the existing value */
102- handleChange(metadata, event);
103- handleMount(metadata);
104- }
111+ handleChange({...metadata, valid: () => checkValidity(metadata)}, event);
112+ handleMount({...metadata, valid: () => checkValidity(metadata)});
113+ /** Sets the current country code to the code of the initial value */
114+ setCurrentCode(metadata.isoCode as ISO2Code);
115+ if (loaded.current && !initialized.current) reset.current = true;
116+ initialized.current = false;
117+ }, [handleChange, handleMount]);
118+
119+ useEffect(() => {
120+ reset.current = !browserLoaded;
121+ return () => {
122+ browserLoaded = false;
123+ }
124+ }, []);
105125
106126 return (
107127 <ReactPhoneInput
0 commit comments