Getting Undefined is not a function in one of the function - react-native

I am very new to React Native. I am trying to make autocomplete text box in React Native. I am using react-native-autocomplete-input plugin. I am reading the data from JSON file. I keep getting this error. user is entering the service name in the text box so they can enter ser and it will show service1 as an option for user to select.
Below is my App.js code:
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import service from './services.json';
import Autocomplete from 'react-native-autocomplete-input';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
class Autocomp extends Component {
static renderServices(coservice) {
const { ser, Location, secondLoc} = coservice;
return (
<View>
<Text style={styles.titleText}>{ser}</Text>
<Text style={styles.openingText}>{secondLoc}</Text>
</View>
);
}
constructor(props) {
super(props);
this.state = {
query: '',
services:[]
};
}
componentDidMount(){
const {results: services} = service;
this.setState({services});
}
findServices(query) {
if (query === '') {
return [];
}
const {services } = this.state;
const regex = new RegExp(`${query.trim()}`, 'i');
return services.filter(coservice=> coservice.ser.search(regex) >= 0);
}
render() {
const { query } = this.state;
const services = this.findservices(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return (
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={services.length === 1 && comp(query, services[0].ser) ? [] : services}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter Services here"
renderItem={({ ser, Phone }) => (
<TouchableOpacity onPress={() => this.setState({ query: ser })}>
<Text style={styles.itemText}>
{ser}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{services.length > 0 ? (
Autocomp.renderServices(services[0])
) : (
<Text style={styles.infoText}>
Enter services
</Text>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
paddingTop: 25
},
autocompleteContainer: {
flex: 1,
left: 0,
position: 'absolute',
right: 0,
top: 0,
zIndex: 1
},
itemText: {
fontSize: 15,
margin: 2
},
descriptionContainer: {
// `backgroundColor` needs to be set otherwise the
// autocomplete input will disappear on text input.
backgroundColor: '#F5FCFF',
marginTop: 25
},
infoText: {
textAlign: 'center'
},
titleText: {
fontSize: 18,
fontWeight: '500',
marginBottom: 10,
marginTop: 10,
textAlign: 'center'
},
directorText: {
color: 'grey',
fontSize: 12,
marginBottom: 10,
textAlign: 'center'
},
openingText: {
textAlign: 'center'
}
});
export default Autocomp;
below is my JSON (services.json) file:
{
"id":1,
"ser": "Service1",
"Location": "TestLoc1",
"Phone":"(999)-921-9292",
"SecondLoc": "TestLoc",
"email":"accrmail#asrclkrec.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Drive, 99999",
"destAddr2": "Test City, Test Drive, 92345"
},
{
"id":1,
"ser": "TestService",
"Location": "TestLoc1",
"Phone":"(999)-921-9292",
"SecondLoc": "TestLoc",
"email":"accrmail#asrclkrec.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Drive, 99999",
"destAddr2": "Test City, Test Drive, 92345"
},
]
Any help will be highly appreciated.

This is just a silly mistake, your function names don't match. You called findservices, but it should be findServices, with an uppercase S
Also, I want to point out that your way of finding suggestions is fine, but there's a bug in it. In your findServices function, you have
const regex = new RegExp(`${query.trim()}`, 'i');
You're constructing a new regular expression from query, but this doesn't always succeed. For example, if user enters a special character such as [, then this line of code will throw an error, because query now contains an open bracket but not a closing bracket ], therefore, a regex cannot be constructed from query. You should change it to something like this:
findServices(query) {
const inputValue = query.trim().toLowerCase();
const inputLength = inputValue.length;
const { services } = this.state;
return inputLength === 0 ? [] : services.filter(ser =>
ser.toLowerCase().slice(0, inputLength) === inputValue);
}

In addition of K.Wu answer, please remove this line const services = this.findservices(query); in render method and placed it inside onChangeText of autocomplete . It should be triggered after user has typing the text on autocomplete box not rendered by default
Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={services.length === 1 && comp(query, services[0].ser) ? [] : services}
defaultValue={query}
onChangeText={text => this.findServices(text}
placeholder="Enter Services here"
renderItem={({ ser, Phone }) => (
<TouchableOpacity onPress={() => this.setState({ query: ser })}>
<Text style={styles.itemText}>
{ser}
</Text>
</TouchableOpacity>
)}
/>

Related

How to save form data into AsyncStorage and then automatically send that data into mongodb when internet is connected

I am trying to save the form submitted data into AsyncStorage and then automatically send that data into mongodb.
I have tried this. But it does not work. First time when i used the setItem it sets the item but now it is not setting the item.
I i put the code above the render then it gives me an error too many renders.
code
import React from 'react';
import { Text, View, Button, StyleSheet, TextInput, TouchableOpacity, Alert, Modal } from 'react-native';
import { useNetInfo } from '#react-native-community/netinfo';
import AsyncStorage from '#react-native-community/async-storage';
const App = () => {
const [text, onChangeText] = React.useState('');
const [number, onChangeNumber] = React.useState('');
const [data, setData] = React.useState('');
const [synched, setSynched] = React.useState('');
const netInfo = useNetInfo();
const submit = () => {
if (netInfo.isConnected) {
var inputData = {
"email": text,
"password": number
}
const storeData = async () => {
try {
await AsyncStorage.setItem("Data", JSON.stringify(inputData))
}
catch (e) {
Alert.alert(e)
}
}
}
else {
var inputData = {
"email": text,
"password": number
}
const storeData = async () => {
try {
await AsyncStorage.setItem("Data", JSON.stringify(inputData))
}
catch (e) {
Alert.alert(e)
}
}
}
}
if (netInfo.isConnected) {
var networkConn = <Text style={{ color: "green" }}>Back Online</Text>
}
else {
networkConn = <Text style={{ color: "red" }}>No internet</Text>
}
return (
<View>
<Modal
animationType="slide"
transparent={true}
visible={modalVisible}
onRequestClose={() => {
Alert.alert("Modal has been closed.");
setModalVisible(!modalVisible);
}}
></Modal>
<Text style={styles.text}>Signup Form</Text>
<TextInput
style={styles.input1}
onChangeText={onChangeText}
placeholder="Enter your email"
/>
<TextInput
style={styles.input1}
onChangeText={onChangeNumber}
placeholder="Enter your password"
secureTextEntry={true}
/>
<View style={styles.btnContainer}>
<TouchableOpacity>
<Button
style={{ fontSize: 20, color: 'green' }}
styleDisabled={{ color: 'red' }}
onPress={submit}
title="Sign Up"
>
</Button>
</TouchableOpacity>
</View>
<View>
<Text style={styles.text1}>{networkConn}</Text>
<Text style={styles.text1}>{synched}</Text>
<Text style={styles.text1}>{data}</Text>
</View>
</View>
)
}
const styles = StyleSheet.create({
text: {
marginTop: 50,
textAlign: 'center',
fontSize: 25,
fontWeight: 'bold',
color: 'blue'
},
text1: {
marginTop: 10,
textAlign: 'center',
fontSize: 12,
color: 'black'
},
input1: {
height: 40,
marginLeft: 50,
marginRight: 50,
marginTop: 30,
borderWidth: 1,
},
btnContainer: {
height: 100,
marginTop: 30,
width: 100,
alignSelf: "center"
},
});
export default App;
Putting data in render is not good idea.
Well you can use this flow:
Store the data offline if internet is not available but with extra parameter like isUploaded : false
Then when internet is back get data from the storage check whether the data parameter i.e isUploaded: false if it is false then upload the data and if you wanna keep it then you can otherwise remove the item from the asyncstorage.

How can I write well formatted code in React Native?

I'm new in React Native. I want to write well formatted code so I can manage big apps easily and I can change existing code with new change.
Right now I'm putting all component in single block, so I'm confused when I want to change in that.
I have made registration screen as below. My code messed up and looks so distorted. Please help me how can I manage my code to good fragments way.
import LoginScreen from './Login';
import React, {Component} from 'react';
import 'react-native-gesture-handler';
import {createAppContainer} from 'react-navigation';
import {createStackNavigator} from 'react-navigation-stack';
import {
SafeAreaView,
StyleSheet,
ScrollView,
View,
Text,
StatusBar,
TextInput,
Button,
Image,
KeyboardAvoidingView,
} from 'react-native';
class RegistrationScreen extends Component {
state = {
textInputTexts: {
username: '',
password: '',
email: '',
phoneno: '',
},
validFlags: {
username: false,
password: false,
email: false,
phoneno: false,
},
};
validateEmail = text => {
console.log(text);
let reg = /^([\w.%+-]+)#([\w-]+\.)+([\w]{2,})$/i;
console.log(reg.test(text));
if (reg.test(text) === false) {
console.log('Email is Not Correct');
return false;
} else {
console.log('Email is Correct');
return true;
}
};
validatePassword = text => {
console.log(text);
var passRegex = new RegExp(
'^(?=.*[a-z])(?=.*[A-Z])(?=.*[0-9])(?=.*[!##$%^&*])(?=.{8,})',
);
console.log(passRegex.test(text));
if (passRegex.test(text) === false) {
console.log('Password is Not Valid');
return false;
} else {
console.log('Password is Correct');
return true;
}
};
validateUsername = text => {
console.log(text);
var usernameRegex = new RegExp('(?=.*[a-fA-F])(?=.{5,})');
console.log(usernameRegex.test(text));
if (usernameRegex.test(text) === false) {
console.log('Username is Not Valid');
return false;
} else {
console.log('Username is Correct');
return true;
}
};
textInputTextChanged = (key, value) => {
var valid = false;
if (key === 'username') {
valid = this.validateUsername(value);
} else if (key === 'password') {
valid = this.validatePassword(value);
} else if (key === 'email') {
valid = this.validateEmail(value);
} else if (key === 'phoneno') {
if (value.length === 10) {
valid = true;
}
}
if (valid) {
console.log('Input is valid');
} else {
console.log('Input is not valid');
}
//this.setState(Object.assign(this.state.textInputTexts, {[key]: value}));
this.setState({
textInputTexts: {...this.state.textInputTexts, [key]: value},
validFlags: {...this.state.validFlags, [key]: valid},
});
//console.log(this.state);
};
signUp = () => {
const {validFlags} = this.state;
console.log('Sign up click');
if (
validFlags.username &&
validFlags.password &&
validFlags.email &&
validFlags.phoneno
) {
// navigate to Login screen
console.log('Go to login screen');
this.props.navigation.navigate('LoginScreen');
}
};
render() {
const {validFlags} = this.state; // this is for constant parameter of this.state to avoid writing this.state.validFlags
console.log(this.state);
const errorImage = (
<Image
source={require('./../images/error-icon.png')}
style={styles.errorImageStyle}
/>
);
return (
<SafeAreaView>
<ScrollView>
<View>
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
placeholder="Username"
autoCapitalize="none"
placeholderTextColor="#a7a7a7"
onChangeText={value =>
this.textInputTextChanged('username', value)
}
/>
{!validFlags.username && errorImage}
</View>
{!validFlags.username && (
<Text style={styles.errorLabelStyle}>
Username must contain 5 alphabets
</Text>
)}
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
placeholder="Password"
autoCapitalize="none"
placeholderTextColor="#a7a7a7"
secureTextEntry={true}
onChangeText={value =>
this.textInputTextChanged('password', value)
}
/>
{!validFlags.password && errorImage}
</View>
{!validFlags.password && (
<Text style={styles.errorLabelStyle}>
Password must contain 8 characters with atleast one special
character, Capital and small characters and numbers
</Text>
)}
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
placeholder="Email Id"
autoCapitalize="none"
placeholderTextColor="#a7a7a7"
onChangeText={value =>
this.textInputTextChanged('email', value)
}
/>
{!validFlags.email && errorImage}
</View>
{!validFlags.email && (
<Text style={styles.errorLabelStyle}>
Email text should be valid email id
</Text>
)}
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
placeholder="Phone No."
autoCapitalize="none"
placeholderTextColor="#a7a7a7"
keyboardType="number-pad"
onChangeText={value =>
this.textInputTextChanged('phoneno', value)
}
/>
{!validFlags.phoneno && errorImage}
</View>
{!validFlags.phoneno && (
<Text style={styles.errorLabelStyle}>
Phone number must contain 10 digits
</Text>
)}
<Button title="Sign Up" onPress={this.signUp} />
</View>
</ScrollView>
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
alignSelf: 'center',
width: 300,
paddingTop: 300,
},
textInput: {
flex: 1,
height: 50,
backgroundColor: '#E2E2E2',
marginTop: 10,
marginRight: 10,
padding: 8,
color: 'black',
borderRadius: 10,
fontSize: 16,
fontWeight: '500',
},
textInputContainer: {
flexDirection: 'row',
alignItems: 'center',
},
errorImageStyle: {
width: 20,
height: 20,
},
errorLabelStyle: {
alignSelf: 'flex-start',
color: 'red',
paddingLeft: 8,
marginBottom: 10,
fontSize: 12,
},
});
const AppNavigator = createStackNavigator(
{
RegistrationScreen: {
screen: RegistrationScreen,
},
LoginScreen: {
screen: LoginScreen,
},
},
{
initialRouteName: 'LoginScreen',
defaultNavigationOptions: {
title: 'App',
},
},
);
export default createAppContainer(AppNavigator);
Best advantages of using react-native is its component driven. You can follow the some of the below mentioned things,
Have separate file for navigation, I see createStackNavigator and other things in registration page, its not good, it must be put in different file.
Separate out styles in different file, each component must have a corresponding style file from where it refers too.
Write as much re-usable components as possible, for ex in your case,
<View style={styles.textInputContainer}>
<TextInput
style={styles.textInput}
placeholder="Email Id"
autoCapitalize="none"
placeholderTextColor="#a7a7a7"
onChangeText={value =>
this.textInputTextChanged('email', value)
}
/>
{!validFlags.email && errorImage}
</View>
Is repeated a lot of times, make it as a separate component and pass only the required props to it, let the common things remain as it is.
For whole application architecture point of view use redux
Use Prettier for code formatting.

React Native Search Dropdown

I'm working on React native app. I'm looking for a searchable dropdown which I need to implement in many places.
Below see below video for reference:
Sample Video
I have implemented below third parties but they are not same as I need:
https://www.npmjs.com/package/react-native-searchable-dropdown
https://www.npmjs.com/package/react-native-searchable-selectbox
https://github.com/toystars/react-native-multiple-select
I tried implementing something similar a while ago and at the time I dropped the idea of having a drop down as it was inconsistent on both platforms & I could not find a perfect solution. I cannot see your video but I think I know where you're going with this.
Here is my advice:
I would create a separate screen that opens on the tap on this component that would be a 'dropdown', and in there create a searchable/filtrable list. You could try doing that using this: https://www.npmjs.com/package/searchable-flatlist, or create your own flatlist, which is super easy and allows for more customization!
EDIT:
If you don't want a separate screen use this: https://www.npmjs.com/package/react-native-searchable-dropdown
try implementing one :
const sports = ["Badminton","Cricket","Chess","Kho-Kho","Kabbadi","Hockey","Boxing","Football","Basketball","Volleyball","Tennis","Table Tennis"];
predict(){
let q = this.state.query;
let arr = sports.filter(ele => ele.toLowerCase().indexOf(q.toLowerCase()) != -1).splice(0,5);
this.setState((prev = this.state)=>{
let obj={};
Object.assign(obj,prev);
obj.predictions.splice(0,obj.predictions.length);
arr.forEach(ele=>{
obj.predictions.push({key : ele});
});
return obj;
});
}
<TouchableWithoutFeedback onPress={()=>{this.setState({done : true})}}>
<ScrollView
keyboardShouldPersistTaps='handled'
contentContainerStyle={style.content}
>
<View>
<TextInput
onChangeText={(text)=>{
this.setState({query : text , done : false});
this.predict();
}}
placeholder="What do you want to play ?"
placeholderTextColor="#A6A4A4"
value = {this.state.query}
onSubmitEditing = {()=>{this.setState({done : true})}}
underlineColorAndroid = "#0098fd"
></TextInput>
<TouchableOpacity onPress={()=>{this.filteredEvents()}}><Icon name="search" color = "#0098fd" size = {20}></Icon></TouchableOpacity>
</View>
{
this.state.predictions.length != 0 && !this.state.done &&
<FlatList
style={styles.predictor_view}
data={this.state.predictions}
extraData = {this.state}
renderItem = {
({item})=>(
<TouchableOpacity
style={styles.predictions}
onPress={()=>{
console.log('ok');
this.setState({query : item.key,done : true});
console.log(this.state);
}}>
<Text>{item.key}</Text>
</TouchableOpacity>
)
}
/>
}
</ScrollView>
</TouchableWithoutFeedback>
I have used react-native-autocomplete-input
I have written a component to help in the dropdown:
customDropDownComponent.js
/*This is an example of AutoComplete Input/ AutoSuggestion Input*/
import React, { Component } from 'react';
import { StyleSheet, Text, TouchableOpacity, View, Button, ScrollView } from 'react-native';
//import all the components we are going to use.
import Autocomplete from 'react-native-autocomplete-input'
//import Autocomplete component
class CustomDropDownComponent extends Component {
constructor(props) {
super(props);
//Initialization of state
//films will contain the array of suggestion
//query will have the input from the autocomplete input
this.state = {
query: '',
data: [],
dataDuplicate:[],
itemSelected: false
};
}
componentDidMount() {
//Loading all data
this.loadData()
}
findElement(query, text, itemSelected) {
//method called everytime when we change the value of the inputquery === '' ||
if (itemSelected === true||query==='') {
//if the query is null or an item is selected then return blank
return [];
}
//making a case insensitive regular expression to get similar value from the data json
const regex = new RegExp(`${query.trim()}`, 'i');
//return the filtered data array according the query from the input
var newList = [];
var result = this.state.IATADup.filter(data => {
if (data.id.search(regex) === 0) {
newList.push(data);
return false;
} else {
return data.id.search(regex) >= 0;
}
});
result = newList.concat(result);
this.props.adjustMargin(1, result.length);//expadnding space in page to make room for dropdown
this.setState({ data: result, query: text, itemSelected: itemSelected });
}
loadData = () => {
var dataToLoad = Commondata.aircraftDetail
dataToLoad.sort(function (a, b) {
if (a.id > b.id) {
return 1;
}
if (b.id > a.id) {
return -1;
}
return 0;
});
this.setState({
dataDuplicate: dataToLoad
})
}
render() {
const { query } = this.state;
const data = this.state.data;
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
var inputContainerStyle = styles.inputContainerStyle;
if (this.props.destinationBorder === "#FF0000") {
inputContainerStyle = styles.inputContainerRedStyle;
}
return (
<View style={styles.container} >
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
flatListProps={{ nestedScrollEnabled: true }}
containerStyle={styles.autocompleteContainer}
listStyle={styles.listStyle}
inputContainerStyle={inputContainerStyle}
data={data}
keyExtractor={(item, i) => { return i }
defaultValue={query}
onChangeText={text => {
//handle input
if (text.trim() === "") this.props.adjustMarginBack();//adjust margin to normal in case of empty searrch element
this.findElement(text, text, false);//search for element
}}
placeholder={en.pick_one}
renderItem={({ item }) => (
//you can change the view you want to show in suggestion from here
<TouchableOpacity onPress={() => {
this.props.adjustMarginBack()
this.setState({ query: item.id, itemSelected: true, data: [] });
}}>
<Text style={styles.itemText}>
{item.id}
</Text>
<Text style={styles.itemSubText}>
{item.name}
</Text>
</TouchableOpacity>
)}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF'
},
autocompleteContainer: {
backgroundColor: '#ffffff',
borderWidth: 0,
},
inputContainerStyle: {
borderWidth: 0.5, borderColor: '#D9D9D9', padding: '1.5%'
},
inputContainerRedStyle: {
borderWidth: 0.5, borderColor: '#FF0000', padding: '1.5%'
},
descriptionContainer: {
flex: 1,
justifyContent: 'center',
padding: '5%'
},
itemText: {
fontSize: 15,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
},
itemSubText: {
fontSize: 10,
paddingTop: 5,
paddingBottom: 5,
margin: 2,
marginLeft: 10
},
infoText: {
textAlign: 'center',
fontSize: 16,
},
listStyle: {
height: 100,
position: "relative",
zIndex: 999
}
});
export default CustomComponent;
Now in the display screen:
app.js
import React, { Component } from 'react';
import { View, Text, ScrollView } from 'react-native';
import CustomDropDownComponent from './CustomDropDownComponent.js'
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
};
}
render() {
return (
<View>
<ScrollView
nestedScrollEnabled={true}
keyboardShouldPersistTaps={'handled'}>
<CustomDropDownComponent /*Handle all inputs and margin resets as props *//>
</ScrollView>
</View>
);
}
}

Type Error in filter function

I am trying to make a autocomplete text box in React Native. I am getting an error in filter function. When the user types the services then the text box should get autocompleted with the full name of the service.The service name is coming from my json file. I am using 'react-native-autocomplete-input' in order to accomplish this. Below is the screen shot of the error:
Below is my App.js code.
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import service from './services.json';
import Autocomplete from 'react-native-autocomplete-input';
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TouchableOpacity,
View
} from 'react-native';
class Autocomp extends Component {
static renderServices(coservice) {
const { ser, Location, secondLoc} = coservice;
return (
<View>
<Text style={styles.titleText}>{ser}</Text>
<Text style={styles.openingText}>{secondLoc}</Text>
</View>
);
}
constructor(props) {
super(props);
this.state = {
query: '',
services:[]
};
}
componentDidMount(){
const {results: services} = service;
this.setState({services});
}
findServices(query) {
const inputValue = query.trim().toLowerCase();
const inputLength = inputValue.length;
const { services } = this.state;
return inputLength === 0 ? [] : services.filter(ser =>ser.toLowerCase().slice(0, inputLength) === inputValue);
}
render() {
const { query } = this.state;
const services = this.findServices(query);
const comp = (a, b) => a.toLowerCase().trim() === b.toLowerCase().trim();
return (
<View style={styles.container}>
<Autocomplete
autoCapitalize="none"
autoCorrect={false}
containerStyle={styles.autocompleteContainer}
data={services.length === 1 && comp(query, services[0].ser) ? [] : services}
defaultValue={query}
onChangeText={text => this.setState({ query: text })}
placeholder="Enter Services here"
renderItem={({ ser, Phone }) => (
<TouchableOpacity onPress={() => this.setState({ query: ser })}>
<Text style={styles.itemText}>
{ser}
</Text>
</TouchableOpacity>
)}
/>
<View style={styles.descriptionContainer}>
{services.length > 0 ? (
Autocomp.renderServices(services[0])
) : (
<Text style={styles.infoText}>
Enter services
</Text>
)}
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: '#F5FCFF',
flex: 1,
paddingTop: 25
},
autocompleteContainer: {
flex: 1,
left: 0,
position: 'absolute',
right: 0,
top: 0,
zIndex: 1
},
itemText: {
fontSize: 15,
margin: 2
},
descriptionContainer: {
// `backgroundColor` needs to be set otherwise the
// autocomplete input will disappear on text input.
backgroundColor: '#F5FCFF',
marginTop: 25
},
infoText: {
textAlign: 'center'
},
titleText: {
fontSize: 18,
fontWeight: '500',
marginBottom: 10,
marginTop: 10,
textAlign: 'center'
},
directorText: {
color: 'grey',
fontSize: 12,
marginBottom: 10,
textAlign: 'center'
},
openingText: {
textAlign: 'center'
}
});
export default Autocomp;
Below is my services.json file
{
"id":1,
"ser": "Service1",
"Location": "TestLoc1",
"Phone":"(999)-921-9292",
"SecondLoc": "TestLoc",
"email":"accrmail#test.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Drive, 99999",
"destAddr2": "Test City, Test Drive, 92345"
},
{
"id":1,
"ser": "TestService",
"Location": "TestLoc1",
"Phone":"(999)-921-9292",
"SecondLoc": "TestLoc",
"email":"accrmail#test.com",
"sourceLat":"33.977806",
"sourceLong":"-117.373261",
"destLatL1":"33.613355",
"destLongL1":"-114.596569",
"destLatL2":"33.761693",
"destLongL2":"-116.971169",
"destAddr1": "Test Drive, 99999",
"destAddr2": "Test City, Test Drive, 92345"
},
]
any help will be highly appreciated. I checked the function. everything looks correct.
Assuming that your json file is as shown here, there are two problems with your code.
Destructuring is wrong. Since you're directly importing an object from a json file as name services, which has not been assigned to any named constant / variable, therefore it cant be destructured.
Therefore you must change the code as
import services from './services.json';
componentDidMount(){
this.setState({services});
}
You're trying to convert a service object toLowerCase here
ser =>ser.toLowerCase()
which needs to be changed to
services.filter(({ser}) => ser.toLowerCase().slice(0, inputLength) === inputValue);

how to design react native OTP enter screen?

I am new in react native design .Let me know how to achieve the screen shown below
is it necessary to use 4 TextInput or possible with one?
You can use just one hidden TextInput element and attach an onChangeText function and fill values entered in a Text view (you can use four different text view of design requires it).
Make sure to focus the TextInput on click of Text view if user click on it
Here I have created a screen with Six text input for otp verfication with Resend OTP functionality with counter timer of 90 sec. Fully tested on both android and ios.
I have used react-native-confirmation-code-field for underlined text input.
Below is the complete code.
import React, { useState, useEffect } from 'react';
import { SafeAreaView, Text, View ,TouchableOpacity} from 'react-native';
import { CodeField, Cursor, useBlurOnFulfill, useClearByFocusCell } from
'react-native-confirmation-code-field';
import { Button } from '../../../components';
import { styles } from './style';
interface VerifyCodeProps {
}
const CELL_COUNT = 6;
const RESEND_OTP_TIME_LIMIT = 90;
export const VerifyCode: React.FC<VerifyCodeProps> = () => {
let resendOtpTimerInterval: any;
const [resendButtonDisabledTime, setResendButtonDisabledTime] = useState(
RESEND_OTP_TIME_LIMIT,
);
//to start resent otp option
const startResendOtpTimer = () => {
if (resendOtpTimerInterval) {
clearInterval(resendOtpTimerInterval);
}
resendOtpTimerInterval = setInterval(() => {
if (resendButtonDisabledTime <= 0) {
clearInterval(resendOtpTimerInterval);
} else {
setResendButtonDisabledTime(resendButtonDisabledTime - 1);
}
}, 1000);
};
//on click of resend button
const onResendOtpButtonPress = () => {
//clear input field
setValue('')
setResendButtonDisabledTime(RESEND_OTP_TIME_LIMIT);
startResendOtpTimer();
// resend OTP Api call
// todo
console.log('todo: Resend OTP');
};
//declarations for input field
const [value, setValue] = useState('');
const ref = useBlurOnFulfill({ value, cellCount: CELL_COUNT });
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
//start timer on screen on launch
useEffect(() => {
startResendOtpTimer();
return () => {
if (resendOtpTimerInterval) {
clearInterval(resendOtpTimerInterval);
}
};
}, [resendButtonDisabledTime]);
return (
<SafeAreaView style={styles.root}>
<Text style={styles.title}>Verify the Authorisation Code</Text>
<Text style={styles.subTitle}>Sent to 7687653902</Text>
<CodeField
ref={ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({ index, symbol, isFocused }) => (
<View
onLayout={getCellOnLayoutHandler(index)}
key={index}
style={[styles.cellRoot, isFocused && styles.focusCell]}>
<Text style={styles.cellText}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
</View>
)}
/>
{/* View for resend otp */}
{resendButtonDisabledTime > 0 ? (
<Text style={styles.resendCodeText}>Resend Authorisation Code in {resendButtonDisabledTime} sec</Text>
) : (
<TouchableOpacity
onPress={onResendOtpButtonPress}>
<View style={styles.resendCodeContainer}>
<Text style={styles.resendCode} > Resend Authorisation Code</Text>
<Text style={{ marginTop: 40 }}> in {resendButtonDisabledTime} sec</Text>
</View>
</TouchableOpacity >
)
}
<View style={styles.button}>
<Button buttonTitle="Submit"
onClick={() =>
console.log("otp is ", value)
} />
</View>
</SafeAreaView >
);
}
Style file for this screen is in given below code:
import { StyleSheet } from 'react-native';
import { Color } from '../../../constants';
export const styles = StyleSheet.create({
root: {
flex: 1,
padding: 20,
alignContent: 'center',
justifyContent: 'center'
},
title: {
textAlign: 'left',
fontSize: 20,
marginStart: 20,
fontWeight:'bold'
},
subTitle: {
textAlign: 'left',
fontSize: 16,
marginStart: 20,
marginTop: 10
},
codeFieldRoot: {
marginTop: 40,
width: '90%',
marginLeft: 20,
marginRight: 20,
},
cellRoot: {
width: 40,
height: 40,
justifyContent: 'center',
alignItems: 'center',
borderBottomColor: '#ccc',
borderBottomWidth: 1,
},
cellText: {
color: '#000',
fontSize: 28,
textAlign: 'center',
},
focusCell: {
borderBottomColor: '#007AFF',
borderBottomWidth: 2,
},
button: {
marginTop: 20
},
resendCode: {
color: Color.BLUE,
marginStart: 20,
marginTop: 40,
},
resendCodeText: {
marginStart: 20,
marginTop: 40,
},
resendCodeContainer: {
flexDirection: 'row',
alignItems: 'center'
}
})
Hope it will be helpful for many. Happy Coding!!
I solved this problem for 6 digit otp by Following Chethan's answer.
Firstly create a array 'otp' initialised with otp = ['-','-','-','-','-','-'] in state,then create a otpVal string in state like this
const [otp, setOtp] = useState(['-', '-', '-', '-', '-', '-']);
const [otpVal, setOtpVal] = useState('');
Now the actual logic of rendering otp boxes willbe as follows.
<TextInput
onChangeText={value => {
if (isNaN(value)) {
return;
}
if (value.length > 6) {
return;
}
let val =
value + '------'.substr(0, 6 - value.length);
let a = [...val];
setOtpVal(a);
setOtp(value);
}}
style={{ height: 0 }}
autoFocus = {true}
/>
<View style={styles.otpBoxesContainer}>
{[0, 1, 2, 3, 4, 5].map((item, index) => (
<Text style={styles.otpBox} key={index}>
{otp[item]}
</Text>
))}
</View>
with styles of otpBoxesContainer and otpBox as below:
otpBoxesContainer: {
flexDirection: 'row'
},
otpBox: {
padding: 10,
marginRight: 10,
borderWidth: 1,
borderColor: lightGrey,
height: 45,
width: 45,
textAlign: 'center'
}
Now , since height of TextInput is set to 0, it doesn't show up to the user but it still takes the input. And we modify and store that input in such a way, that we can show it like values are entered in separate input boxes.
I was facing the same problem and I managed to develop a nicely working solution. Ignore provider, I am using it for my own purposes, just to setup form values.
Behavior:
User enters first pin number
Next input is focused
User deletes a number
Number is deleted
Previous input is focused
Code
// Dump function to print standard Input field. Mine is a little customised in
// this example, but it does not affects the logics
const CodeInput = ({name, reference, placeholder, ...props}) => (
<Input
keyboardType="number-pad"
maxLength={1}
name={name}
placeholder={placeholder}
reference={reference}
textAlign="center"
verificationCode
{...props}
/>
);
// Logics of jumping between inputs is here below. Ignore context providers it's for my own purpose.
const CodeInputGroup = ({pins}) => {
const {setFieldTouched, setFieldValue, response} = useContext(FormContext);
const references = useRef([]);
references.current = pins.map(
(ref, index) => (references.current[index] = createRef()),
);
useEffect(() => {
console.log(references.current);
references.current[0].current.focus();
}, []);
useEffect(() => {
response &&
response.status !== 200 &&
references.current[references.current.length - 1].current.focus();
}, [response]);
return pins.map((v, index) => (
<CodeInput
key={`code${index + 1}`}
name={`code${index + 1}`}
marginLeft={index !== 0 && `${moderateScale(24)}px`}
onChangeText={(val) => {
setFieldTouched(`code${index + 1}`, true, false);
setFieldValue(`code${index + 1}`, val);
console.log(typeof val);
index < 3 &&
val !== '' &&
references.current[index + 1].current.focus();
}}
onKeyPress={
index > 0 &&
(({nativeEvent}) => {
if (nativeEvent.key === 'Backspace') {
const input = references.current[index - 1].current;
input.focus();
}
})
}
placeholder={`${index + 1}`}
reference={references.current[index]}
/>
));
};
// Component caller
const CodeConfirmation = ({params, navigation, response, setResponse}) => {
return (
<FormContext.Provider
value={{
handleBlur,
handleSubmit,
isSubmitting,
response,
setFieldTouched,
setFieldValue,
values,
}}>
<CodeInputGroup pins={[1, 2, 3, 4]} />
</FormContext.Provider>
);
};
try this package https://github.com/Twotalltotems/react-native-otp-input
it works best with both the platforms
try this npm package >>> react-native OTP/Confirmation fields
below is the screenshot of the options available, yours fall under underline example.
below is the code for underline example.
import React, {useState} from 'react';
import {SafeAreaView, Text, View} from 'react-native';
import {
CodeField,
Cursor,
useBlurOnFulfill,
useClearByFocusCell,
} from 'react-native-confirmation-code-field';
const CELL_COUNT = 4;
const UnderlineExample = () => {
const [value, setValue] = useState('');
const ref = useBlurOnFulfill({value, cellCount: CELL_COUNT});
const [props, getCellOnLayoutHandler] = useClearByFocusCell({
value,
setValue,
});
return (
<SafeAreaView style={styles.root}>
<Text style={styles.title}>Underline example</Text>
<CodeField
ref={ref}
{...props}
value={value}
onChangeText={setValue}
cellCount={CELL_COUNT}
rootStyle={styles.codeFieldRoot}
keyboardType="number-pad"
textContentType="oneTimeCode"
renderCell={({index, symbol, isFocused}) => (
<View
// Make sure that you pass onLayout={getCellOnLayoutHandler(index)} prop to root component of "Cell"
onLayout={getCellOnLayoutHandler(index)}
key={index}
style={[styles.cellRoot, isFocused && styles.focusCell]}>
<Text style={styles.cellText}>
{symbol || (isFocused ? <Cursor /> : null)}
</Text>
</View>
)}
/>
</SafeAreaView>
);
};
const styles = StyleSheet.create({
root: {padding: 20, minHeight: 300},
title: {textAlign: 'center', fontSize: 30},
codeFieldRoot: {
marginTop: 20,
width: 280,
marginLeft: 'auto',
marginRight: 'auto',
},
cellRoot: {
width: 60,
height: 60,
justifyContent: 'center',
alignItems: 'center',
borderBottomColor: '#ccc',
borderBottomWidth: 1,
},
cellText: {
color: '#000',
fontSize: 36,
textAlign: 'center',
},
focusCell: {
borderBottomColor: '#007AFF',
borderBottomWidth: 2,
},
})
export default UnderlineExample;
source : Github Link to above Code
Hope it helps! :)
There is a plugin React Native Phone Verification works both with iOS and Android (Cross-platform) with this you can use verification code picker matching with your requirement.
We used to do it with single hidden input field as described in #Chethan’s answer. Now since RN already supports callback on back button on Android platform (since RN 0.58 or even before). It is possible to do this with just normal layout of a group of text inputs. But we also need to consider the text input suggestion on iOS or auto fill on Android. Actually, we have develop a library to handle this. Here is blog to introduce the library and how to use it. And the source code is here.
#kd12345 : You can do it here in:
onChangeText={(val) => {
setFieldTouched(`code${index + 1}`, true, false);
setFieldValue(`code${index + 1}`, val);
console.log(typeof val);
// LITTLE MODIFICATION HERE
if(index < 3 && val !== '') {
references.current[index + 1].current.focus();
// DO WHATEVER
}
}}