ReactNative: Custom CheckBox for both Android and iOS - react-native

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

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;

React native SectionList not updating

I am developing a sample app as part of learning react-native, in which it has a ColorForm that accepts the value from a TextInput and with the help of Props, the data will be sent to the Main view, such as App. The App has a SectionList that doesn't update with the new value inputs from ColorForm.
Following is the source code that I have used:
App.js
import React, { Component } from 'react'
import {
StyleSheet,
SectionList,
Text,
Alert
} from 'react-native'
import ColorButton from './components/ColorButton'
import ColorForm from './components/ColorForm'
class App extends Component {
constructor() {
super()
this.state = {
backgroundColor: 'blue',
availableColors: ['red', 'green', 'yellow', 'pink']
}
this.changeColor = this.changeColor.bind(this)
this.newColor = this.newColor.bind(this)
}
changeColor(backgroundColor) {
this.setState({ backgroundColor })
}
newColor(color) {
const colors = [
...this.state.availableColors,
color
]
this.setState(colors)
}
render() {
const { backgroundColor, availableColors } = this.state
return (
<SectionList style={styles.list}
backgroundColor={backgroundColor}
sections={[{ title: "Header", data: availableColors }]}
renderSectionHeader={({ section }) => (
<ColorForm onNewColor={this.newColor} />
)}
renderItem={({ item }) => (
<ColorButton backgroundColor={item} onSelect={this.changeColor} />
)}
keyExtractor={(item, index) => index}
/>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
paddingTop: 20
},
list: {
marginTop: 40
}
})
export default App
ColorForm.js
import React, { Component } from 'react'
import {
View,
Text,
StyleSheet,
TextInput
} from 'react-native'
import PropTypes from 'prop-types'
export default class ColorForm extends Component {
constructor() {
super()
this.state = {
txtColor: ''
}
this.submit = this.submit.bind(this)
}
submit() {
this.props.onNewColor(this.state.txtColor.toLowerCase())
this.setState({ txtColor: '' })
}
render() {
return (
<View style={styles.container}>
<TextInput
style={styles.txtInput}
placeholder="Enter a color..."
onChangeText={(txtColor) => this.setState({ txtColor })}
value={this.state.txtColor}
/>
<Text style={styles.button} onPress={this.submit}>Add</Text>
</View>
)
}
}
ColorForm.propTypes = {
onNewColor: PropTypes.func.isRequired
}
const styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'space-around',
backgroundColor: 'lightgrey',
height: 70,
paddingTop: 20
},
txtInput: {
flex: 1,
margin: 5,
padding: 5,
borderWidth: 2,
fontSize: 20,
borderRadius: 5,
backgroundColor: 'snow'
},
button: {
backgroundColor: 'darkblue',
margin: 5,
padding: 5,
alignItems: 'center',
justifyContent: 'center',
color: 'white',
fontSize: 20
}
})
Can anyone help me to sort out the issue? Thanks in advance.
It looks like the state is not being updated in your method newColor, since availableColors is an array u can use push operation to add new value to the array update the code as below and it will work.
newColor(color) {
console.log('adding', color)
const colors = this.state.availableColors
colors.push(color)
this.setState(colors)
}

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

React Native Animated lagging - Prevent Re-Rendering by function call

Context
Im want to get a countdown value which I then want to pass as prop to a separate component animating a progress bar. the data.json contains two type of elements:
text
question
the text will present the user an explanation. when the user pushes the button another text can be shown or a question. when it is a question the user will have certain amount of time for the answer. the progress bar will indicate the remaining time and shall only be shown when the current element is a question.
Problem
My problem is that the screen update for the current value is super slow and I can't see the reason. here is a simplified version of my code. the progress bar component will replace the <Text>{this.state.value}</Text> within the renderButtons function
Versions
I am running react native 0.60.
Update 1
I found out that when I trigger the countdown function by an onPress event, it works. but if I call it directly it doesn't. Why is that? How could I achieve it without the onPress
Update 2
the problem is that due to the animation the state value gets updated an by that a serenader gets triggered, which causes the function to be called each time, which is causing the lagging. this workaround seems to help but it feels ugly.
what would be a better approach to handle the re-rendering issue?
countdown = type => {
if (!this.state.countdownRunning) {
this.setState({ countdownRunning: true });
if (type === 'question') {
this.state.percent.addListener(({ value }) => this.setState({ value }));
Animated.timing(
// Animate value over time
this.state.percent, // The value to drive
{
toValue: 0, // Animate to final value of 1
duration: 25000,
easing: Easing.linear
}
).start(() => {
console.log('Animation DONE');
this.setState({ value: 100, percent: new Animated.Value(100) });
this.onPressNext();
this.setState({ countdownRunning: false });
}); // Start the animation
}
}
};
Lagging
import React, { Component } from 'react';
import { View, Text, StyleSheet, Animated, Easing } from 'react-native';
import { Button } from 'react-native-paper';
import Card from '../components/Card';
import data from '../data/data.json';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
},
surface: {
padding: 20,
margin: 20,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
elevation: 12
}
});
class Home extends Component {
constructor(props) {
super(props);
this.countdown = this.countdown.bind(this);
this.state = {
i: 0,
questions: data,
value: 100,
percent: new Animated.Value(100)
};
}
onPressNext = () => {
const { i } = this.state;
if (i < 17) {
this.setState({ i: i + 1 });
} else {
this.setState({ i: 0 });
}
};
onPressBack = () => {
const { i } = this.state;
if (i > 0) {
this.setState({ i: i - 1 });
} else {
this.setState({ i: 17 });
}
};
countdown = type => {
if (type === 'question') {
this.state.percent.addListener(({ value }) => this.setState({ value }));
Animated.timing(
// Animate value over time
this.state.percent, // The value to drive
{
toValue: 0, // Animate to final value of 1
duration: 25000,
easing: Easing.linear
}
).start(); // Start the animation
}
};
renderButtons = type => {
if (type === 'question') {
this.countdown(type);
return (
<View>
<Text>{this.state.value}</Text>
</View>
);
}
return (
<View style={{ flexDirection: 'row' }}>
<Button mode="text" onPress={() => this.onPressBack()}>
Zurück
</Button>
<Button mode="contained" onPress={() => this.onPressNext(type)}>
Weiter
</Button>
</View>
);
};
render() {
const { i, questions } = this.state;
const { type, header, content } = questions.data[i.toString()];
return (
<View style={styles.container}>
<View style={{ flex: 2, justifyContent: 'flex-end' }}>
<Card>
<Text>{header}</Text>
<Text>{content}</Text>
</Card>
</View>
<View style={{ flex: 2 }}>{this.renderButtons(type)}</View>
</View>
);
}
}
export default Home;
No Lagging
import React, { Component } from 'react';
import { View, Text, StyleSheet, Animated, Easing } from 'react-native';
import { Button } from 'react-native-paper';
import Card from '../components/Card';
import data from '../data/data.json';
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF'
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5
},
surface: {
padding: 20,
margin: 20,
borderRadius: 10,
alignItems: 'center',
justifyContent: 'center',
elevation: 12
}
});
class Home extends Component {
constructor(props) {
super(props);
this.countdown = this.countdown.bind(this);
this.state = {
i: 0,
questions: data,
value: 100,
percent: new Animated.Value(100)
};
}
onPressNext = () => {
const { i } = this.state;
if (i < 17) {
this.setState({ i: i + 1 });
} else {
this.setState({ i: 0 });
}
};
onPressBack = () => {
const { i } = this.state;
if (i > 0) {
this.setState({ i: i - 1 });
} else {
this.setState({ i: 17 });
}
};
countdown = type => {
if (type === 'question') {
this.state.percent.addListener(({ value }) => this.setState({ value }));
Animated.timing(
// Animate value over time
this.state.percent, // The value to drive
{
toValue: 0, // Animate to final value of 1
duration: 25000,
easing: Easing.linear
}
).start(); // Start the animation
}
};
renderButtons = type => {
if (type === 'question') {
return (
<View>
<Text>{this.state.value}</Text>
<Button mode="contained" onPress={() => this.countdown(type)}>
Weiter
</Button>
</View>
);
}
return (
<View style={{ flexDirection: 'row' }}>
<Button mode="text" onPress={() => this.onPressBack()}>
Zurück
</Button>
<Button mode="contained" onPress={() => this.onPressNext(type)}>
Weiter
</Button>
</View>
);
};
render() {
const { i, questions } = this.state;
const { type, header, content } = questions.data[i.toString()];
return (
<View style={styles.container}>
<View style={{ flex: 2, justifyContent: 'flex-end' }}>
<Card>
<Text>{header}</Text>
<Text>{content}</Text>
</Card>
</View>
<View style={{ flex: 2 }}>{this.renderButtons(type)}</View>
</View>
);
}
}
export default Home;

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