How can I write well formatted code in React Native? - 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.

Related

Results do not update after a change of state

I have a problem, when I do a search, I get the data from my API, the first time I do a search, everything is fine, all the data is displayed. However, when I do a second search immediately, nothing is updated.
I put in console.log, and I see that I'm getting this data back, yet the display is not updated.
import React, { Component } from "react";
import { SafeAreaView, StyleSheet } from "react-native";
import Search from "./Component/Search";
export default class App extends Component {
render() {
return (
<SafeAreaView style={styles.container}>
<Search />
</SafeAreaView>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
}
});
import React from "react";
import { View, TextInput, Button, FlatList, StyleSheet } from "react-native";
import LivreItem from "../Component/LivreItem";
class Search extends React.Component {
constructor(props) {
super(props);
this.inputValue = "";
this.state = { livres: [] };
}
searchBooks = async () => {
const key = "&key=XXXXXXXXXXXXXXXXXXXXXXX";
const url = "https://www.googleapis.com/books/v1/volumes?q=" + this.inputValue + key;
return fetch(url)
.then(response => response.json())
.catch(e => {
console.log("Une erreur s'est produite");
console.log(e);
});
};
getBooks = () => {
if (this.inputValue.length > 0) {
this.searchBooks()
.then(data => this.setState({ livres: data.items }))
.catch(reject => console.log(reject));
}
};
changeText = text => {
this.inputValue = text;
};
render() {
return (
<View style={styles.header_container}>
<View style={styles.sub_container}>
<TextInput
onChangeText={text => this.changeText(text)}
style={styles.input}
placeholder="Ex: Harry Potter"
/>
<Button
style={styles.button}
title="Rechercher"
onPress={() => this.getBooks()}
/>
</View>
<FlatList
style={styles.list}
data={this.state.livres}
keyExtractor={(item, index) => item + index}
renderItem={({ item }) => <LivreItem livre={item.volumeInfo} />}
/>
</View>
);
}
}
const styles = StyleSheet.create({
sub_container: {
justifyContent: "space-between",
flexDirection: "row",
marginTop: 30,
paddingRight: 10,
paddingLeft: 10
},
header_container: {
flex: 1,
flexDirection: "column",
padding: 10
},
input: {
borderRadius: 4,
borderWidth: 0.5,
borderColor: "#d6d7da",
width: 150,
paddingLeft: 5
},
button: {
borderRadius: 4
},
list: {
paddingLeft: 15,
paddingRight: 15
}
});
export default Search;
import React from "react";
import { View, StyleSheet, Image, Text } from "react-native";
class LivreItem extends React.Component {
constructor(props) {
super(props);
this.state = { livre: this.props.livre};
this.description =
this.state.livre.description === null || this.state.livre.description === undefined
? "Pas de description disponible"
: this.state.livre.description;
this.img = this.state.livre.imageLinks;
this.image =
this.img === undefined ||
this.img.smallThumbnail === undefined ||
this.img.smallThumbnail === null
? null
: this.state.livre.imageLinks.smallThumbnail;
}
render() {
return (
<View style={styles.content}>
<View>
<Image style={styles.image} source={{ uri: this.image }} />
<Image style={styles.image} source={this.image} />
</View>
<View style={styles.content_container}>
<Text style={styles.titre}>{this.state.livre.title}</Text>
<Text style={styles.description} numberOfLines={4}>
{this.description}
</Text>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
content: {
height: 125,
flexDirection: "row",
marginTop: 15
},
content_container: {
flexDirection: "column",
flexShrink: 1,
marginLeft: 10
},
image: {
width: 100,
height: 100
},
titre: {
fontWeight: "bold",
flexWrap: "wrap"
},
description: {
flexWrap: "wrap"
}
});
export default LivreItem;
Thanks.
Configure the prop extraData in Flatlist component ala:
<FlatList
extraData={this.state.livres}
/>
Pass a boolean value to the FlatList extraData.
<FlatList
extraData={this.state.refresh}
/>

Redirecting user based on his authentication

In my react native application, I want to check if a user has already logged in before, then he must be redirected to 'Home' directly without asking him for credentials again. The 'Home' component consists of logout button in a sidebar.
My current code works fine for new user logging, but I am stuck on how to check if again the user open the application, his login token should persist and he must be redirected to 'Home' directly.
Here is my code:
import React, { Component } from 'react'
import { Text, View, Image, TextInput, TouchableOpacity, ScrollView, AsyncStorage, ToastAndroid } from 'react-native'
import axios from 'axios';
export default class Login extends Component {
constructor(){
super();
this.state = {
username: '',
password: '',
isLoggedIn: false,
loginChecked: false,
}
}
componentDidMount(){
this.getItem('userID');
}
//function to extract storage token. Any name can be given ot it.
async getItem(item){
console.log('method ran login screen');
console.log(this.state.isLoggedIn)
try{
const value = await AsyncStorage.getItem(item);
if(value !== null){
this.setState({
isLoggedIn: true,
loginChecked: true
})
}
else{
this.setState({
isLoggedIn: false,
loginChecked: true
})
}
}
catch(error){
console.log(error)
}
console.log(this.state.isLoggedIn)
}
//function to remove storage token
async removeItem(item){
try{
const value = await AsyncStorage.removeItem(item);
if(value == null){
this.setState({
isLoggedIn: false
})
}
}
catch(error){
//handle errors here
}
}
userLogin = () => {
if(this.state.username != '' && this.state.password != ''){
axios.post('http://bi.servassure.net/api/login', {
username: this.state.username,
password: this.state.password
})
.then(res => {
let userToken = res.data.token;
console.log(res.data);
if(res.data.success){
AsyncStorage.setItem('userID', userToken);
this.setState({
isLoggedIn: true
})
this.props.navigation.navigate('Home');
}
else{
ToastAndroid.showWithGravity(
'Invalid login' ,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
}
})
.catch(err => {
console.log(err);
});
}
else{
ToastAndroid.showWithGravity(
'Please Enter Credentials' ,
ToastAndroid.SHORT,
ToastAndroid.CENTER
);
}
}
logOut(){
this.removeItem('userID');
}
render() {
return (
<ScrollView>
<View style={{justifyContent:'center', alignItems:'center'}}>
<View style={{marginVertical:20}}>
<Text>
Login to your account
</Text>
</View>
<View>
<TextInput
style={{width: 300, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 10, marginVertical: 10}}
onChangeText={(username) => this.setState({username})}
placeholder='username'
autoCapitalize = 'none'
/>
<TextInput
style={{width: 300, height: 50, borderColor: 'gray', borderWidth: 1, borderRadius: 10}}
onChangeText={(password) => this.setState({password})}
placeholder='password'
secureTextEntry={true}
/>
</View>
<View style={{justifyContent: 'center', alignItems: 'center'}}>
<TouchableOpacity
style={{width: 300, height: 50, borderWidth:1, borderRadius: 50, borderColor: 'gray', justifyContent: 'center', alignItems: 'center', marginVertical: 10}}
onPress={this.userLogin}>
<Text>
LOGIN
</Text>
</TouchableOpacity>
<Text>Forget Password</Text>
</View>
</View>
</ScrollView>
)
}
}
Also, I have a SplashScreen before login:
import React, { Component } from 'react'
import { Text, View } from 'react-native'
export default class SplashScreen extends Component {
componentDidMount(){
setTimeout( () => {
this.props.navigation.navigate('Login')
}, 2000)
}
render() {
return (
<View style={styles.viewStyles}>
<Text style={styles.textStyles}> My App </Text>
</View>
)
}
}
const styles = {
viewStyles: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'orange'
},
textStyles: {
color: 'white',
fontSize: 40,
fontWeight: 'bold'
}
}
I am bit new to react native, please help to figure this out.
Do something like this in your Login.js file:
import {AsyncStorage} from react-native;
After getting the response success of login API you can do this:
AsyncStorage.setItem('userID', responsejson.user_detail.userID);
in the same way, you can store the token also
AsyncStorage.setItem('token', responsejson.user_detail.token);
Then in your splashscreen.js, import AsyncStorage the same way as above and then place this code in componentWillMount() or componentDidMount()
of your splashscreen.js
var value = AsyncStorage.getItem('token');
value.then((e)=>{
if (e == '' || e == null ){
this.props.navigation.replace('Login')
}else {
this.props.navigation.replace('Home')
}
})
Explanation: When splashscreen.js is loaded then it checks for the token in asyncstorage and if it gets the token navigate to Home screen else navigate to Login screen.
Import the React Navigation library and use the Switch Navigator. It was designed to handle app navigation based on the login status of the user.
This article explains everything with examples

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

Getting Undefined is not a function in one of the function

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>
)}
/>

Reactnative Issue

I have a very simple login screen where the user enters userid and password. when the user clicks on login button then flow moves to next screen if entered userid/password combination is correct.
on click of login button, I am calling an API with userid and password as input. API returns a count of matching records from DB in JSON format.If the count is >0 then login is successful.
Issue: I have to click login button twice. when I click on login button nothing happens but if I click it again then I get fail message or move to the second screen depending on userid/password combination. I am copying my code below. I'll appreciate any help.
import React, { Component } from 'react';
import {
AppRegistry,
View,
Image,
StyleSheet,
KeyboardAvoidingView,
TextInput,
TouchableOpacity,
Text,
StatusBar,
Alert
} from 'react-native';
import {
StackNavigator
} from 'react-navigation';
export default class Login extends Component {
constructor(props) {
super(props)
this.state = {
email: '',
password: '',
uCount: -1,
data: []
};
}
getData(){
var url="https://myurl/verifySubscription.php?email=" + this.state.email + "&password=" + this.state.password;
console.log("URL:", url);
return fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
uCount: responseJson.count
})
})
.catch((error) => {
console.error(error);
});
}
async _onPressButton() {
await this.getData();
console.log("uCount:", this.state.uCount);
if (this.state.uCount < 1) {
Alert.alert('Login Failed: Incorrect email or password')
} else {
this.props.navigation.navigate('LoginSuccess', { email: this.state.email, password: this.state.password})
}
}
render() {
return (
<KeyboardAvoidingView behavior="padding" style={styles.wrapper}>
<View style={styles.topView}>
<Image style={styles.imageStyle}
source={require('../images/main.jpg')}
/>
</View>
<View style={styles.bottomView}>
<StatusBar
barStyle="light-content"
/>
<TextInput style={styles.Input}
placeholder="Email"
placeholderTextColor="rgba(255,255,255,0.7)"
keyBoardType='email-address'
returnKeyType="next"
autoCapitalize="none"
autoCorrect={false}
onSubmitEditing={() => this.passwordInput.focus()}
onChangeText={(text) => this.setState({email:text})}
/>
<TextInput style={styles.Input}
placeholder="Password"
placeholderTextColor="rgba(255,255,255,0.7)"
returnKeyType="go"
secureTextEntry
autoCapitalize="none"
autoCorrect={false}
ref={(next) => this.passwordInput = next}
onChangeText={(text) => this.setState({password:text})}
/>
<TouchableOpacity style={styles.button1Container} onPress={ this._onPressButton.bind(this) }>
<Text style={styles.buttonText}>
Login
</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.button2Container}>
<Text style={styles.buttonText}>
Sign up
</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
);
}
}
const styles = StyleSheet.create({
wrapper: {
backgroundColor: '#4A7AA5',
flex: 1
},
topView: {
flexGrow: 1
},
imageStyle: {
width: null,
flex: 1
},
bottomView: {
padding: 20
},
Input: {
height:40,
backgroundColor: 'rgba(255,255,255,0.3)',
marginBottom: 10,
color: '#FFF',
paddingHorizontal: 10
},
button1Container: {
backgroundColor: 'rgba(200,200,255,0.3)',
padding: 10
},
buttonText: {
textAlign: 'center',
color: '#FFF',
fontWeight: '700'
},
button2Container: {
padding: 10
}
});
Issue maybe related to the async process when you press the button. fetch will return a promise which then can be resolve after response is completed. Try this, not using await (es2017), just es6.
getData(){
var url="https://myurl/verifySubscription.php?email=" + this.state.email +
"&password=" + this.state.password;
console.log("URL:", url);
return fetch(url);
}
_onPressButton() {
this.getData().then((response) => response.json())
.then((responseJson) => {
const cnt = responseJson.cnt;
if (cnt < 1) {
Alert.alert('Login Failed: Incorrect email or password')
} else {
this.props.navigation.navigate('LoginSuccess', { email:
this.state.email, password: this.state.password})
}
});
}