React Native Search Dropdown - react-native

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

Related

How can I get the value from custom radio button component in parent class

I have a custom radio button component. I am importing that to my parent form to create a dynamic fields of radio button from JSON file. I have multiple other views too. I am getting these views values and creating a JSON array in my parent form. I am stuck in how to get my custom radio buttons values and pass them to my method where i am creating JSON array of values.Here is my custom component radio button code
import React, { Component } from "react";
import { View, TouchableOpacity, Text, StyleSheet } from "react-native";
export default class RadioButton extends Component {
state = {
value: null,
};
render() {
const { PROP } = this.props;
const { value } = this.state;
return (
<View>
{PROP.map((res) => {
return (
<View key={res.key} style={styles.container}>
<Text style={styles.radioText}>{res.text}</Text>
<TouchableOpacity
style={styles.radioCircle}
onPress={() => {
this.setState({
value: res.text,
});
}}
>
{value === res.text && <View style={styles.selectedRb} />}
</TouchableOpacity>
</View>
);
})}
<Text> Selected: {this.state.value} </Text>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
marginBottom: 15,
alignItems: "center",
flexDirection: "row",
justifyContent: "space-between",
},
radioText: {
marginRight: 35,
color: "#000",
},
radioCircle: {
height: 20,
width: 20,
marginRight: 10,
borderRadius: 100,
borderWidth: 2,
borderColor: "#3740ff",
alignItems: "center",
justifyContent: "center",
},
selectedRb: {
width: 15,
height: 15,
borderRadius: 15,
backgroundColor: "#3740ff",
},
result: {
marginTop: 20,
color: "white",
fontWeight: "600",
backgroundColor: "#F3FBFE",
},
});
This is my main class.
import React, { Component } from "react";
import CheckBox from "#react-native-community/checkbox";
import { View, TextInput, Button, StyleSheet, Text } from "react-native";
const data = require("../json/registration.json");
import MyRadioButton from "../component/MyRadioButton";
class Registration extends Component {
constructor(props) {
super(props);
this.state = {
itemstorender: [],
inputData: [],
checked: "",
};
}
addValues = (value, index) => {
let dataArray = this.state.inputData;
let checkBool = false;
if (dataArray.length !== 0) {
dataArray.forEach((element) => {
if (element.index === index) {
element.value = value;
checkBool = true;
}
});
}
if (checkBool) {
this.setState({
inputData: dataArray,
});
} else {
dataArray.push({ value: value, index: index });
this.setState({
inputData: dataArray,
});
// console.log('Data',dataArray);
}
};
getValues = () => {
console.log("Data", this.state.inputData);
};
componentDidMount() {
this.renderData();
}
hideComponent(data) {
// console.log("checkd",data)
this.setState({
checked: data,
});
console.log(this.state.checked);
}
renderData = () => {
const result = data.info;
var itemstorenderLocal = [];
for (var i = 0; i < result.length; i++) {
if (result[i].element == "TextInput") {
let i_id = result[i].id;
console.log("Ids : ", i_id);
itemstorenderLocal.push(
<TextInput
key={result[i].id}
placeholder={result[i].label}
onChangeText={(value) => this.addValues(value, i_id)}
/>
);
this.setState({ itemstorenderLocal });
}else if (result[i].element == "RadioButtons") {
let i_id = result[i].id;
// let options = console.log("Ids : ", i_id);
itemstorenderLocal.push(
<Text>{result[i].label}</Text>,
<View style={styles.container}>
<MyRadioButton
PROP={result[i].options}
/>
</View>
);
this.setState({ itemstorenderLocal });
} else if (result[i].element == "button") {
itemstorenderLocal.push(
<Button
key={result[i].id}
title={result[i].label}
onPress={() => this.getValues()}
/>
);
}
}
this.setState({
itemstorender: itemstorenderLocal,
});
};
render() {
return <View>{this.state.itemstorender}</View>;
}
}
export default Registration;

Element type is invalid: expected a string with FlatList

Iam new to react-native, and are playing around with UI Kitten (https://akveo.github.io/react-native-ui-kitten/)
It has a default ChatListScreenBase that renders a list. It looks like this:
import React, {Component} from 'react';
import {
StyleSheet,
View,
ListView
} from 'react-native';
import ThemeService from '../util/ThemeService';
import api from '../util/ApiMock';
export default class ChatListScreenBase extends Component {
constructor(props) {
super(props);
let ds = new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
});
let data = api.getUserMsgList(api.userId);
this.state = {
dataSource: ds.cloneWithRows(data)
};
}
render() {
let Header = ThemeService.getChatListHeaderComponent();
return (
<View style={styles.container}>
<Header/>
<ListView
style={styles.list}
automaticallyAdjustContentInsets={false}
dataSource={this.state.dataSource}
renderRow={(row) => this._renderMsgItem(row)}
/>
</View>
)
}
_renderMsgItem(msg) {
let user = api.getUserInfo(msg.from);
let ChatItem = ThemeService.getChatItemComponent();
msg.text = msg.text.length > 25 ? msg.text.substring(0,23)+'...' : msg.text;
return (
<ChatItem
user={user}
message={msg}
onClick={(user) => this._openChat(user)}
/>
);
}
_openChat(user) {
this.props.navigator.push({
screen: ThemeService.getChatScreen(true),
passProps: {
userId: user.id
}
});
}
}
const styles = StyleSheet.create({
container:{
flex: 1,
},
list: {
paddingTop: 10
},
});
I would like to substitute the data this screen renders, with my own code, like this:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
FlatList
} from 'react-native';
import ajax from '../util/ApiMock';
export class ChatListScreenBase extends Component {
constructor(props) {
super(props);
this.state = {
themeIndex: ThemeService.getCurrentThemeIndex()
}
}
state = {
data: []
}
async componentDidMount() {
const data = await ajax.getDataTest();
this.setState({data});
}
render() {
return (
<View style={styles.container} >
<Text style={styles.h2text}>
Black Order
</Text>
<FlatList
data={this.state.data}
showsVerticalScrollIndicator={false}
renderItem={({item}) =>
<View style={styles.flatview}>
<Text style={styles.name}>{item.title}</Text>
</View>
}
keyExtractor={item => item.title}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
marginTop: 50,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
h2text: {
marginTop: 10,
fontFamily: 'Helvetica',
fontSize: 36,
fontWeight: 'bold',
},
flatview: {
justifyContent: 'center',
paddingTop: 30,
borderRadius: 2,
},
name: {
fontFamily: 'Verdana',
fontSize: 18
},
email: {
color: 'red'
}
});
When I do this, I keep getting this error:
"Element type is invalid: expected a string (for built-in components) or a class/function (for composite components) but got: undefined. Check the render method of ChatListScreenBase."
I think that the problem is with the <FlatList>, but I'am not sure what type its expecting?
When I call the webservice it returns:
{"users":[{"_id":"5d0fcd8fe1dbfd7c23424e09","title":"180710","type":1,"published":"5"},{"_id":"5d0fcd8fe1dbfd7c23424e0a","title":"180705","type":3,"publiched":"5"},{"_id":"5d0fcd8fe1dbfd7c23424e0b","title":"Mr.
Nick","type":2,"publiched":"5"}]}
Kind regards.
For the looks of your API response you are setting data as object with key users and then trying to loop over it.
Instead update componentDidmount:
async componentDidMount() {
const resp = await ajax.getDataTest();
this.setState({ data: resp.users });
}

ReactNative: Custom CheckBox for both Android and iOS

As said here <checkBox/> is only possible for android but i want to implement single code for both android and iOS (without using any node packages). For this i'm proceeding with views as below
import React, { Component } from 'react';
import { Platform, View, TouchableOpacity } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
checkSelected: 1
}
}
checkClick(id) {
this.setState({
checkSelected: id
})
}
render() {
const products = [{
id: 1
},
{
id: 2
},
{
id: 3
}];
return (
products.map((val) => {
return (
<TouchableOpacity key={val.id} onPress={this.checkClick.bind(this, val.id)}>
<View style={{
height: 24,
width: 24,
border: 12,
borderWidth: 2,
borderColor: '#000',
alignItems: 'center',
justifyContent: 'center',
}}>
{
val.id == this.state.checkSelected ?
<View style={{
height: 12,
width: 12,
border: 6,
backgroundColor: '#000',
}} />
: null
}
</View>
</TouchableOpacity>
)
})
);
}
}
This output will be like this
This approach is like RadioButton but i want to follow the <View/> approach for CheckBox also. For this i've implemented like this
import React, { Component } from 'react';
import { View, TouchableOpacity } from 'react-native';
export default class App extends Component {
constructor(props) {
super(props);
this.state = {
checkSelected: []
}
}
checkClick(id) {
let todos = [...this.state.checkSelected]; //creating the copy
//adding new data
todos.push({
checkId: id
});
//updating the state value
this.setState({ checkSelected: todos }, () => {
alert('state: ' + JSON.stringify(this.state.checkSelected));
});
}
render() {
const products = [{
id: 1
},
{
id: 2
},
{
id: 3
}];
return (
products.map((val) => {
return (
<TouchableOpacity key={val.id} onPress={this.checkClick.bind(this, val.id)}>
<View style={{
height: 24,
width: 24,
border: 12,
borderWidth: 2,
borderColor: '#000',
alignItems: 'center',
justifyContent: 'center',
}}>
{
this.state.checkSelected.map((checkIds) => {
{
checkIds.checkId == val.id ?
<View style={{
height: 12,
width: 12,
border: 6,
backgroundColor: '#000',
}} />
: null
}
})
}
</View>
</TouchableOpacity>
)
})
);
}
}
In this approach i'm storing clicked id's in state and trying to select all checkboxes which id's are in state but i'm unable to do that. Can any one suggest me to select multiple check boxes.
Solution
Make new CheckBox component.
Call CheckBox components with right props.
Change your state value depending on CheckBox's clicked prop.
import React, { Component } from 'react';
import { View, TouchableOpacity } from 'react-native';
class CheckBox extends Component {
  constructor(props) {
    super(props);
    this.state = {isCheck: false};
  }
  checkClicked = async () => {
    await this.setState(prevState => ({
      isCheck: !prevState.isCheck,
    })); // setState is async function.
    // Call function type prop with return values.
    this.props.clicked && this.props.clicked(this.props.value, this.state.isCheck);
  }
  render() {
    return (
      <TouchableOpacity onPress={this.checkClicked} style={this.props.style}>
        <View style={{
          height: 24,
          width: 24,
          borderWidth: 2,
          borderColor: '#000',
          alignItems: 'center',
          justifyContent: 'center',
        }}>
          <View style={{
            height: 12,
            width: 12,
            backgroundColor: this.state.isCheck ? '#000' : '#FFF',
          }} />
        </View>
      </TouchableOpacity>
    )
  }
}
const products = [
  {
    id: 1
  },
  {
    id: 2
  },
  {
    id: 3
  }
];
export default class CheckBoxScreen extends Component {
constructor(props) {
super(props);
this.state = {
checkSelected: [],
    }
}
  toggleCheckBox = (id, isCheck) => {
    let { checkSelected } = this.state;
    if (isCheck) {
      checkSelected.push(id);
    } else { // remove element
      var index = checkSelected.indexOf(id);
      if (index > -1) {
        checkSelected.splice(index, 1);
      }
    }
    this.setState({ checkSelected });
    alert(this.state.checkSelected); // logging
  }
render() {
    const checkboxs = products.map(({id}) =>
      <CheckBox style={{marginTop: 50,}}key={id} value={id} clicked={(id, isCheck) => this.toggleCheckBox(id, isCheck)}></CheckBox>
    )
return (
      <View style={{flex: 1, alignItems: 'center'}}>
        {checkboxs}
      </View>
);
}
}
It will work and you can use my example to other ways what you want.
In a different soluion, i would recommend to make a checkbox component , which will have properties id and onCheckBoxValueChange. Both will take initial values from parent component, and on every change, the state of local component will change, returning a callback with the id of the selected.
Child:
constructor(props) {
super(props);
this.state = {
value: this.props.value
};
}
onCheckBoxValueChange(id) {
this.setState({ value: !this.state.value });
this.props.onCheckBoxValueChange(id, !this.state.value);
}
render() {
return (
<ViewStyledAsCheckBox
style={styles.checkBox}
isChecked={this.state.value}
onPress={() => this.onCheckBoxValueChange(this.props.id)}
/>
Parent will call child component like this:
<CheckBoxComponent
id={1}
onCheckBoxValueChange={(id, value) =>
this.doSomethingWithChangedValueInThisSpecificChekbox(id, value)
}
/>
<CheckBoxComponent
id={2}
onCheckBoxValueChange={(id, value) =>
this.doSomethingWithChangedValueInThisSpecificChekbox(id, value)
}
/>
Let me know if this works for you or want more information as this is more of a template than real implementation

How to set state key value pair dynamically in react native?

I am developing an ecommerce app where i need to show product options like Size, color etc.
These options are coming dynamically from API.
I am using Picker component of react native to show option values so that user can choose a value.
Now issue is i select a value, it does not reflect in picker value because i don't know that how to define state variables for pickers dynamically.
so that i can update state values when picker onchangevalue is called.
Initially state looks like this:
state = {
options: {}
}
Now i want that this should dynamically add options to this blank object so it will look like this:
state = {
options: {
Size: '0.5"',
Colour: 'White Dudhiya'
}
}
And after I'll create it then How to access these state variables in any other function.
Please correct me if i am moving in wrong direction and share the correct way to achieve the same.
Please let me know if you need more information from my side.
Any help appreciated.
Thanks
Edit:
This is how i am rendering product options and their values dynamically:
renderOptions(data) {
if (data.options.length > 0) {
var allOptions = data.options.map((item) => {
if (item.product_option_value.length > 0) {
return (
<View key={item.option_id} style={{ marginLeft: 10, marginRight: 10, marginBottom: 15, width: windowWidth - 20 }}>
<MyAppText>{item.name}</MyAppText>
<View style={{ borderWidth: 1, borderColor: '#E8E8E8', marginTop: 5, height: 50 }}>
<Picker
selectedValue={this.state.options[item.name]}
style={{ height: 50, width: '100%' }}
onValueChange={(itemValue, itemIndex) =>
this.setState(() => {
key = item.name;
return {key: itemValue};
})
}
>
{this.renderOptionValues(item)}
</Picker>
</View>
</View>
);
} else {
return;
}
});
return allOptions;
}
}
renderOptionValues(option) {
if (option.product_option_value.length > 0) {
return (option.product_option_value.map((optionValue) => {
return (
<Picker.Item key={optionValue.option_value_id} label={entities.decode(optionValue.name)} value={optionValue.option_value_id} />
);
}));
}
}
I call renderOptions() function inside my render() function.
Take a look at the following implementation, I just tried with some hypothetical values and it worked fine.
class DynamicOptions extends React.Component {
constructor(props) {
super(props);
this.state = {};
}
renderOptions = (options = []) => {
return options.map(option => {
return <Picker.Item label={option.label} value={option.value} key={option.key}/>
});
}
render() {
const {
options
} = this.props;
console.log(this.state);
return ( <
View style = {{ flex: 1 }} >
{
options.map((item, index) => {
return (
<View style={{flex: 1}}>
{
options.map((item, index) => {
return (
<View key={`item-${index}`}>
<Text>{item.name}</Text>
<Picker
selectedValue={this.state[item.name]}
style={{ height: 50, width: '100%' }}
onValueChange={(listItem, index) => {
this.setState({
[item.name]: listItem
});
}}
>
{ this.renderOptions(item.options) }
</Picker>
</View>
)
})
}
</View>
)
}
}
export default DynamicOptions;
Following data I have used to render options (I am not sure what data you are using)
const options = [
{
name: 'Color',
options: [{label: 'Red', value: 'Red', key: 'Red'}, {label: 'Green', value: 'Green', key: 'Green'}]
},
{
name: 'Size',
options: [{label: '1', value: '1', key: '1'}, {label: '2', value: '2', key: '2'}]
}
];
This is how I have called that function
<DynamicOptions options={options}/>
Hope this will help!
according to https://stackoverflow.com/a/35398253/2083099 , I believe you need something like this,
import React, { Component } from 'react';
import { Platform, StyleSheet, Text, View, FlatList, Image, TouchableOpacity, Dimensions, Picker } from 'react-native';
export default class Test extends Component {
constructor() {
super();
this.state = {
services: [],
selectedService: 'a'
}
}
componentDidMount() {
//replace api request instead of setTimeout and then change the services data with setState
setTimeout(() => {
this.setState({
services: [ 'one', 'two', 'three', 'four', 'five' ]
})
}, 3000)
}
render() {
return (
<View>
<Text>Pick a service</Text>
<Picker
selectedValue={this.state.selectedService}
onValueChange={(service) => (this.setState({ selectedService: service }))} >
{this.state.services.map((s, i) => {
return <Picker.Item key={i} value={s} label={s} />
})}
</Picker>
</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>
)}
/>