From 32c967466639edbbd86c0c388339b914d2e6b5c3 Mon Sep 17 00:00:00 2001 From: Pawel Wrzosek Date: Mon, 22 Jun 2020 12:39:10 +0200 Subject: [PATCH 1/6] use Picker from `react-native-community` instead of deprecated RN built-in component fixes #120 --- lib/countryPicker.js | 3 ++- package.json | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/lib/countryPicker.js b/lib/countryPicker.js index 1879ebee..9b25e339 100644 --- a/lib/countryPicker.js +++ b/lib/countryPicker.js @@ -1,5 +1,6 @@ import React, { Component } from 'react'; -import { Text, TouchableOpacity, View, Modal, Picker } from 'react-native'; +import { Text, TouchableOpacity, View, Modal } from 'react-native'; +import {Picker} from '@react-native-community/picker'; import PropTypes from 'prop-types'; import Country from './country'; diff --git a/package.json b/package.json index ab4450df..a6fe2c51 100644 --- a/package.json +++ b/package.json @@ -26,7 +26,8 @@ "dependencies": { "google-libphonenumber": "^3.2.2", "lodash": "^4.17.4", - "prop-types": "^15.5.10" + "prop-types": "^15.5.10", + "@react-native-community/picker": "^1.6.5" }, "peerDependencies": { "react-native": ">= 0.25" From 721a02dc5387e81df5951e5d06a113c78ef0d9ea Mon Sep 17 00:00:00 2001 From: Pawel Wrzosek Date: Fri, 26 Jun 2020 13:31:05 +0200 Subject: [PATCH 2/6] fix: prevent from unnecessary state updates --- lib/index.js | 66 +++++++++++++++++++++++++++++----------------------- 1 file changed, 37 insertions(+), 29 deletions(-) diff --git a/lib/index.js b/lib/index.js index db864957..f919c158 100644 --- a/lib/index.js +++ b/lib/index.js @@ -46,7 +46,9 @@ export default class PhoneInput extends Component { componentDidUpdate() { const { value, disabled } = this.props; - this.setState({ disabled }); + if (disabled != null && disabled !== this.state.disabled) { + this.setState({disabled}); + } if (value && value !== this.state.value) { this.setState({ value }); @@ -183,16 +185,18 @@ export default class PhoneInput extends Component { const TextComponent = this.props.textComponent || TextInput; return ( - - - + disabled={disabled} + > + + + )} { @@ -211,23 +215,25 @@ export default class PhoneInput extends Component { /> - { - this.picker = ref; - }} - selectedCountry={iso2} - onSubmit={this.selectCountry} - buttonColor={this.props.pickerButtonColor} - buttonTextStyle={this.props.pickerButtonTextStyle} - cancelText={this.props.cancelText} - cancelTextStyle={this.props.cancelTextStyle} - confirmText={this.props.confirmText} - confirmTextStyle={this.props.confirmTextStyle} - pickerBackgroundColor={this.props.pickerBackgroundColor} - itemStyle={this.props.pickerItemStyle} - onPressCancel={this.props.onPressCancel} - onPressConfirm={this.props.onPressConfirm} - /> + {this.props.shouldShowCountryPicker && ( + { + this.picker = ref; + }} + selectedCountry={iso2} + onSubmit={this.selectCountry} + buttonColor={this.props.pickerButtonColor} + buttonTextStyle={this.props.pickerButtonTextStyle} + cancelText={this.props.cancelText} + cancelTextStyle={this.props.cancelTextStyle} + confirmText={this.props.confirmText} + confirmTextStyle={this.props.confirmTextStyle} + pickerBackgroundColor={this.props.pickerBackgroundColor} + itemStyle={this.props.pickerItemStyle} + onPressCancel={this.props.onPressCancel} + onPressConfirm={this.props.onPressConfirm} + /> + )} ); } @@ -265,11 +271,13 @@ PhoneInput.propTypes = { confirmText: PropTypes.string, confirmTextTextStyle: styleType, disabled: PropTypes.bool, - allowZeroAfterCountryCode: PropTypes.bool + allowZeroAfterCountryCode: PropTypes.bool, + shouldShowCountryPicker: PropTypes.bool }; PhoneInput.defaultProps = { initialCountry: "us", disabled: false, - allowZeroAfterCountryCode: true + allowZeroAfterCountryCode: true, + shouldShowCountryPicker: true }; From 1eee69b74d1d763e32e9e7707f33b1942fc24c65 Mon Sep 17 00:00:00 2001 From: SergeySlutskiyGowombat <68944076+SergeySlutskiyGowombat@users.noreply.github.com> Date: Sun, 20 Sep 2020 19:34:59 +0300 Subject: [PATCH 3/6] fix Maximum update depth exceeded --- lib/countryPicker.js | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/lib/countryPicker.js b/lib/countryPicker.js index 9b25e339..1a03f1b2 100644 --- a/lib/countryPicker.js +++ b/lib/countryPicker.js @@ -35,9 +35,11 @@ export default class CountryPicker extends Component { } componentDidUpdate() { - this.setState({ - selectedCountry: this.props.selectedCountry, - }); + if(this.props.selectedCountry !== this.state.selectedCountry) { + this.setState({ + selectedCountry: this.props.selectedCountry, + }); + } } selectCountry(selectedCountry) { @@ -71,9 +73,8 @@ export default class CountryPicker extends Component { } onValueChange(selectedCountry) { - this.setState({ - selectedCountry, - }); + this.selectCountry(selectedCountry); + this.props.onSubmit(selectedCountry); } show() { From c9ae447249a7f0ae746e9f9f7b9cc98326739824 Mon Sep 17 00:00:00 2001 From: SergeySlutskiyGowombat <68944076+SergeySlutskiyGowombat@users.noreply.github.com> Date: Sun, 20 Sep 2020 19:38:35 +0300 Subject: [PATCH 4/6] Added to instalation react-native-community/picker --- README.md | 1 + 1 file changed, 1 insertion(+) diff --git a/README.md b/README.md index b50c73b1..07552129 100644 --- a/README.md +++ b/README.md @@ -10,6 +10,7 @@ Phone input box for React Native ``` npm i react-native-phone-input --save +npm install @react-native-community/picker --save ``` ## Basic Usage From f82c2942be5c4d182a13ac26c62a467c349747cb Mon Sep 17 00:00:00 2001 From: SergeySlutskiyGowombat <68944076+SergeySlutskiyGowombat@users.noreply.github.com> Date: Sun, 27 Sep 2020 18:04:05 +0300 Subject: [PATCH 5/6] Update index.js --- lib/index.js | 194 +++++++++++++++++++++++++++++++++------------------ 1 file changed, 128 insertions(+), 66 deletions(-) diff --git a/lib/index.js b/lib/index.js index f919c158..2eacfa08 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,55 +13,98 @@ export default class PhoneInput extends Component { Country.setCustomCountriesData(json); } + getPickerData() { + return PhoneNumber.getAllCountries().map((country, index) => ({ + key: index, + image: Flags.get(country.iso2), + label: country.name, + dialCode: `+${country.dialCode}`, + iso2: country.iso2 + })); + } + + getAllCountries() { + return PhoneNumber.getAllCountries(); + } + + getFlag(iso2) { + return Flags.get(iso2); + } + constructor(props, context) { super(props, context); this.onChangePhoneNumber = this.onChangePhoneNumber.bind(this); this.onPressFlag = this.onPressFlag.bind(this); this.selectCountry = this.selectCountry.bind(this); - this.getFlag = this.getFlag.bind(this); - this.getISOCode = this.getISOCode.bind(this); + this.constructNumber = this.constructNumber.bind(this); + this.prepareValue = this.prepareValue.bind(this); - const { countriesList, disabled, initialCountry } = this.props; + const { countriesList, disabled, initialCountry, value = '', } = this.props; + const countryData = PhoneNumber.getCountryDataByCode(initialCountry); + const dialCode = (countryData && countryData.dialCode) || ''; if (countriesList) { Country.setCustomCountriesData(countriesList); } - const countryData = PhoneNumber.getCountryDataByCode(initialCountry); this.state = { iso2: initialCountry, + dialCode, disabled, - formattedNumber: countryData ? `+${countryData.dialCode}` : "", - value: null, - inputValue: "", + inputValue: this.constructNumber({ + iso2: initialCountry, + dialCode, + value: this.prepareValue({value, dialCode}) + }), }; } componentDidMount() { - if (this.props.value) { + if (this.props.value && this.props.value !== this.state.inputValue) { this.updateFlagAndFormatNumber(this.props.value); } } componentDidUpdate() { - const { value, disabled } = this.props; - if (disabled != null && disabled !== this.state.disabled) { + const { + value = '', + disabled = null } = this.props; + + if (disabled) { this.setState({disabled}); } - if (value && value !== this.state.value) { - this.setState({ value }); - this.updateFlagAndFormatNumber(value); + if (value && value !== this.state.inputValue) { + this.updateFlagAndFormatNumber( + this.prepareValue({ dialCode: this.state.dialCode, value}) + ); + } + } + + constructNumber({dialCode, value, iso2}) { + const {useCountryCode, autoFormat} = this.props; + const number = `+${dialCode}${value}`; + if(autoFormat) { + const formatted = this.format(number, iso2); + return useCountryCode ? formatted : formatted.replace(`+${dialCode}`, ""); } + return useCountryCode ? number : value; + } + + prepareValue({dialCode, value = ""}) { + const clearedValue = value.replace(/\s/gm, ''); + return clearedValue.replace(`+${dialCode}`, ''); } onChangePhoneNumber(number) { + const actionAfterSetState = this.props.onChangePhoneNumber ? () => { this.props.onChangePhoneNumber(number); } : null; + this.updateFlagAndFormatNumber(number, actionAfterSetState); } @@ -74,40 +117,21 @@ export default class PhoneInput extends Component { } } - getPickerData() { - return PhoneNumber.getAllCountries().map((country, index) => ({ - key: index, - image: Flags.get(country.iso2), - label: country.name, - dialCode: `+${country.dialCode}`, - iso2: country.iso2 - })); - } - getCountryCode() { - const countryData = PhoneNumber.getCountryDataByCode(this.state.iso2); - return countryData ? countryData.dialCode : null; - } - - getAllCountries() { - return PhoneNumber.getAllCountries(); - } - - getFlag(iso2) { - return Flags.get(iso2); + return this.state.iso2; } getDialCode() { - return PhoneNumber.getDialCode(this.state.formattedNumber); + return this.state.dialCode; } getValue() { - return this.state.formattedNumber.replace(/\s/g,''); + return this.state.inputValue; } getNumberType() { return PhoneNumber.getNumberType( - this.state.formattedNumber, + this.state.inputValue, this.state.iso2 ); } @@ -119,14 +143,23 @@ export default class PhoneInput extends Component { selectCountry(iso2) { if (this.state.iso2 !== iso2) { const countryData = PhoneNumber.getCountryDataByCode(iso2); + const {inputValue, dialCode} = this.state; + if (countryData) { this.setState( { iso2, - formattedNumber: `+${countryData.dialCode}` + dialCode: countryData.dialCode, + inputValue: this.constructNumber({ + iso2, + dialCode: countryData.dialCode, + value: this.prepareValue({ + value: inputValue, + dialCode, + }) + }) }, () => { - this.updateFlagAndFormatNumber(this.state.inputValue) if (this.props.onSelectCountry) this.props.onSelectCountry(iso2); } ); @@ -135,40 +168,65 @@ export default class PhoneInput extends Component { } isValidNumber() { - if (this.state.inputValue.length < 3) return false; + const {inputValue, iso2} = this.state; + if (inputValue < 3) return false; return PhoneNumber.isValidNumber( - this.state.formattedNumber, - this.state.iso2 + inputValue, + iso2 ); } - format(text) { - return this.props.autoFormat - ? PhoneNumber.format(text, this.state.iso2) - : text; + format(text, iso2) { + return PhoneNumber.format(text, iso2); } updateFlagAndFormatNumber(number, actionAfterSetState = null) { - const { allowZeroAfterCountryCode, initialCountry } = this.props; - let iso2 = this.getISOCode() || initialCountry; - let formattedPhoneNumber = number; - if (number) { - const countryCode = this.getCountryCode(); - if (formattedPhoneNumber[0] !== "+" && countryCode !== null) { - formattedPhoneNumber = '+' + countryCode.toString() + formattedPhoneNumber.toString(); + const {useCountryCode, allowZeroAfterCountryCode} = this.props; + const {dialCode, iso2} = this.state; + + let newInputValue = number; + let newDialCode = dialCode; + let newIso2 = iso2; + + if(useCountryCode) { + const isoCode = PhoneNumber.getCountryCodeOfNumber(number); + if(isoCode !== iso2) { + if(isoCode) { + newIso2 = isoCode; + const countryData = PhoneNumber.getCountryDataByCode(newIso2); + newDialCode = countryData ? countryData.dialCode : dialCode; + } else { + newIso2 = ''; + newDialCode = ''; + } } - formattedPhoneNumber = allowZeroAfterCountryCode - ? formattedPhoneNumber - : this.possiblyEliminateZeroAfterCountryCode(formattedPhoneNumber); - iso2 = PhoneNumber.getCountryCodeOfNumber(formattedPhoneNumber); + newInputValue = this.prepareValue({ + dialCode: newDialCode, + value: number + }); } - this.setState({ iso2, formattedNumber: formattedPhoneNumber, inputValue: number }, actionAfterSetState); + + const formatted = this.constructNumber({ + dialCode: newDialCode, value: newInputValue, iso2: newIso2 + }); + + const inputValue = allowZeroAfterCountryCode + ? formatted + : this.possiblyEliminateZeroAfterCountryCode(formatted); + + this.setState({ + inputValue, + dialCode: newDialCode, + iso2: newIso2 + }, actionAfterSetState); } possiblyEliminateZeroAfterCountryCode(number) { - const dialCode = PhoneNumber.getDialCode(number); - return number.startsWith(`${dialCode}0`) - ? dialCode + number.substr(dialCode.length + 1) + const {dialCode} = this.state; + const formatted = this.prepareValue({value: number, dialCode}); + + return formatted.startsWith('0') + ? `+${dialCode}${formatted.replace(/0/gm, '')}` : number; } @@ -181,11 +239,11 @@ export default class PhoneInput extends Component { } render() { - const { iso2, inputValue, disabled } = this.state; + const { iso2, disabled } = this.state; const TextComponent = this.props.textComponent || TextInput; return ( - {this.props.shouldShowCountryPicker && ( + {(this.props.shouldShowCountryPicker && this.props.useCountryCode) && ( - {this.props.shouldShowCountryPicker && ( + {(this.props.shouldShowCountryPicker && this.props.useCountryCode) && ( { this.picker = ref; @@ -272,12 +330,16 @@ PhoneInput.propTypes = { confirmTextTextStyle: styleType, disabled: PropTypes.bool, allowZeroAfterCountryCode: PropTypes.bool, - shouldShowCountryPicker: PropTypes.bool + shouldShowCountryPicker: PropTypes.bool, + useCountryCode: PropTypes.bool, + autoFormat: PropTypes.bool, }; PhoneInput.defaultProps = { initialCountry: "us", disabled: false, allowZeroAfterCountryCode: true, - shouldShowCountryPicker: true + shouldShowCountryPicker: true, + useCountryCode: true, + autoFormat: false }; From 9f2f3065b08b84e2fbd97a21c53ffe71cf18c496 Mon Sep 17 00:00:00 2001 From: SergeySlutskiyGowombat <68944076+SergeySlutskiyGowombat@users.noreply.github.com> Date: Sun, 27 Sep 2020 18:08:00 +0300 Subject: [PATCH 6/6] structural fixes --- lib/index.js | 45 ++++++++++++++++++++++----------------------- 1 file changed, 22 insertions(+), 23 deletions(-) diff --git a/lib/index.js b/lib/index.js index 2eacfa08..b7a25364 100644 --- a/lib/index.js +++ b/lib/index.js @@ -13,24 +13,6 @@ export default class PhoneInput extends Component { Country.setCustomCountriesData(json); } - getPickerData() { - return PhoneNumber.getAllCountries().map((country, index) => ({ - key: index, - image: Flags.get(country.iso2), - label: country.name, - dialCode: `+${country.dialCode}`, - iso2: country.iso2 - })); - } - - getAllCountries() { - return PhoneNumber.getAllCountries(); - } - - getFlag(iso2) { - return Flags.get(iso2); - } - constructor(props, context) { super(props, context); @@ -69,7 +51,8 @@ export default class PhoneInput extends Component { componentDidUpdate() { const { value = '', - disabled = null } = this.props; + disabled = null + } = this.props; if (disabled) { this.setState({disabled}); @@ -98,13 +81,11 @@ export default class PhoneInput extends Component { } onChangePhoneNumber(number) { - const actionAfterSetState = this.props.onChangePhoneNumber ? () => { this.props.onChangePhoneNumber(number); } : null; - this.updateFlagAndFormatNumber(number, actionAfterSetState); } @@ -117,8 +98,26 @@ export default class PhoneInput extends Component { } } + getPickerData() { + return PhoneNumber.getAllCountries().map((country, index) => ({ + key: index, + image: Flags.get(country.iso2), + label: country.name, + dialCode: `+${country.dialCode}`, + iso2: country.iso2 + })); + } + getCountryCode() { - return this.state.iso2; + return this.state.dialCode; + } + + getAllCountries() { + return PhoneNumber.getAllCountries(); + } + + getFlag(iso2) { + return Flags.get(iso2); } getDialCode() { @@ -341,5 +340,5 @@ PhoneInput.defaultProps = { allowZeroAfterCountryCode: true, shouldShowCountryPicker: true, useCountryCode: true, - autoFormat: false + autoFormat: true };