Adding data to FlatList clears the whole list - react-native

I am new to react-native. I have a flatlist, that takes data from "state". This works. Then, I added a new function in order to add new data to the flatlist (additionally to the existing data).
As soon as I click on the "delete" button ( don't mind the name) the data of the flatlist is being deleted completely. I want the output to be like
Object 1
Object 2
Object 3 (has been added after button click)
What am I doing wrong? Can you please explain me the reason?
EDIT: I get this warning, but no error.
VirtualizedList: missing keys for items, make sure to specify a key or
id property on each item or provide a custom keyExtractor.,
import React, { Component } from "react";
import { Text, View, StyleSheet, Button, FlatList } from "react-native";
class App extends Component {
state = {
counter: 0,
initialElements: [
{ id: "0", text: "Object 1" },
{ id: "1", text: "Object 2" },
],
};
render() {
const currentCounter = this.state.counter;
const exampleState = this.state.initialElements;
const addElement = () => {
let newArray = [...exampleState, { id: "2", text: "Object 3" }];
this.setState({
initialElements: [newArray],
});
};
return (
<View style={styles.container}>
<View style={styles.counter}>
<Text style={styles.textStyle}>{currentCounter}</Text>
</View>
<View style={styles.deleteButton}>
<Button
title="Hello"
onPress={() =>
this.setState({
counter: currentCounter + 1,
})
}
></Button>
<View style={styles.space}></View>
<Button title="Delete" onPress={addElement}></Button>{" "}
{/* as soon as I click here, the content of the list is being deleted */}
</View>
<FlatList
style={styles.listStyle}
key={(item) => item.id}
data={exampleState}
renderItem={(item) => <Text>{item.item.text}</Text>}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
backgroundColor: "#fff",
justifyContent: "center",
flex: 1,
flexDirection: "column",
alignItems: "center",
},
counter: {
flexDirection: "row",
justifyContent: "center",
},
deleteButton: {
flexDirection: "row",
margin: 5,
marginTop: 100,
},
space: {
width: 5,
height: 5,
},
textStyle: {
margin: 80,
fontSize: 100,
},
listStyle: {
flexDirection: "column",
},
item: {
padding: 10,
fontSize: 18,
height: 44,
},
});
export default App;

I fixed it by changing the addElement to function to:
const addElement = () => {
var newArray = [...exampleState, {id : 2, text : "Object 3"}];
this.setState( {
initialElements: newArray
});
}
So, basically I changed
initialElements: [newArray]
to
initialElements: newArray
because newArray is already an Array, so no need to wrap it again.

Related

Dynamic textinput inside Flatlist State update

From the sample code below i am trying to render textbox along with content inside Flatlist renderItem method and simultaneously trying to update textbox value.
Bad luck is on device after typing a single character, keyboard goes down.
Please find relevant Snack expo at : https://snack.expo.io/BJGMscqsS
Here is code :
import * as React from "react";
import {
View,
Text,
FlatList,
TextInput
} from "react-native";
export default class App extends React.Component {
state = {
data: []
};
updateItem = async (index, innerindex, itemAttributeVal) => {
console.log(index, innerindex, itemAttributeVal);
console.log(this.state.data);
let { data } = this.state;
data[index][innerindex].Value = itemAttributeVal;
this.setState({
data
});
console.log("newtry", this.state.data);
};
componentDidMount = async () => {
const listdata =
{
totalRow: "2",
coddindata: [
{
"SNo": "1",
"Item": "10",
"Material Code": "SERVICE LABOUR1",
"Invoice Description": "Labour for Services1",
"Invoice Item Amt": "765000.00",
"Approved Quantity": "85",
},
{
"SNo": "2",
"Item": "20",
"Material Code": "SERVICE LABOUR1",
"Invoice Description": "Labour for Services2",
"Invoice Item Amt": "810000.00",
"Approved Quantity": "90",
}
]
}
;
const codingData = listdata.coddindata;
var finalarr = [];
var finalarr1 = [];
codingData.map(datavalue => {
finalarr1 = [];
Object.keys(datavalue).map((key, val) => {
finalarr1.push({ Key: key, Value: datavalue[key] });
});
finalarr.push(finalarr1);
});
this.setState({
data: [...this.state.data, ...finalarr],
totalCount: listdata.totalRow
});
console.log(this.state.data);
};
render() {
return (
<View style={{ flex: 1,padding: 20}}>
<FlatList
style={{ padding: 20 }}
data={this.state.data}
renderItem={({ item, index }) =>
item.map((element, innerindex) => {
// console.log(" innerindex ",innerindex);
const inputstateval = this.state.data[index][innerindex].Value;
return (
<View
key={Math.random().toString()}
style={{
alignItems: "center",
height: 30,
justifyContent: "center",
flexDirection: "row",
lineHeight: 4
}}
>
<View style={{ flex: 1, alignSelf: "stretch" }}>
<Text>{element.Key}</Text>
</View>
{/* { element.Key != "total_amount" ? */}
{element.Key !== "Approved Quantity" ? (
<View
style={{ flex: 2, alignSelf: "stretch", marginTop: 3 }}
>
<Text>{element.Value}</Text>
</View>
) : (
<View
style={{ flex: 2, alignSelf: "stretch", marginTop: 3 }}
>
<TextInput
// defaultValue={element.Value}
placeholder={element.Key}
onChangeText={text => {
console.log("in onChangeText--- ");
this.updateItem(index, innerindex, text);
}}
value={this.state.data[index][innerindex].Value}
style={{
paddingTop: 1,
fontSize: 16,
borderWidth: 1,
height: 30,
marginTop: -6
}}
/>
</View>
)}
</View>
);
})
}
keyExtractor={item => Math.random().toString()}
/>
</View>
);
}
}
In your code, you just need to fix the lines 87, and 132 to something like this:
87: key={'key'}
132: keyExtractor={(item) => ''+item}
So react knows that even after render, those text inputs are the same as the ones before the setState update.
Still, I cant manage to fix this same bug in my project.

How to select a single radio button at a time in react native flatlist

I am using react-native-radio-button to implement a flatlist with radio button as shown in the image.
[![enter image description here][1]][1]
But i am unable to select a single radio button at a time in flatlist.
Here is my code
import React from 'react';
import { StyleSheet, View, Text, FlatList, TouchableOpacity, Image, LayoutAnimation, UIManager, Platform } from 'react-native';
import RadioButton from 'react-native-radio-button';
import PHARMACY from './api/mockPharmacyList';
import { Colors, Fonts } from '../../styles';
interface IState {
selected: number;
selectedIndex: number;
data: any;
expanded: boolean;
checked: boolean;
}
export class TransferToPharmacy extends React.Component<{}, IState> {
constructor(props) {
super(props);
this.state = {
data: PHARMACY,
selected: 0,
selectedIndex: 0,
expanded: true,
checked: false,
};
this.onPress = this.onPress.bind(this);
this.renderItem = this.renderItem.bind(this);
this.renderSeparator = this.renderSeparator.bind(this);
this._keyExtractor = this._keyExtractor.bind(this);
this.changeLayout = this.changeLayout.bind(this);
this.onCheck = this.onCheck.bind(this);
if (Platform.OS === 'android') {
UIManager.setLayoutAnimationEnabledExperimental(true);
}
}
changeLayout = () => {
LayoutAnimation.configureNext(LayoutAnimation.Presets.easeInEaseOut);
this.setState({ expanded: false });
}
getInitialState() {
return {
value: 0,
};
}
onPress(value) {
this.setState({ selected: value });
}
_keyExtractor = (item, index) => item.id;
onCheck = () => {
const { checked } = this.state;
if (checked === true) {
this.setState({ checked: false });
} else {
this.setState({ checked: true });
}
}
renderItem(data) {
const { item } = data;
return (
<View style={styles.itemBlock}>
<TouchableOpacity >
<View style={styles.arrowIconStyle}>
<Text style={styles.pharmacyText}>{item.name}</Text>
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === 0}
onPress={this.onPress}
/>
</View>
</TouchableOpacity>
<Text>{item.key1}</Text>
<View style={{ height: this.state.expanded === true ? 90 : 0, overflow: 'hidden' }}>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/map_pic_icon.png')} />
<Text style={styles.itemText}>{item.location}</Text>
</View>
<View style={{ flexDirection: 'row', marginTop: 5, padding: 10 }}>
<Image style={[styles.itemImage, { width: 15, height: 15, margin: 1 }]} source={require('./images/phone_icon.png')} />
<Text style={styles.itemText}>{item.phone}</Text>
</View>
</View>
</View>
);
}
renderSeparator() {
return <View style={styles.separator} />;
}
render() {
return (
<View style={styles.container} >
<Text style={styles.textStyle}>Transfer to Pharmacy</Text>
<View style={styles.childViewStyle}>
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
/>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
textStyle: {
fontSize: 18,
color: Colors.black,
marginTop: 30,
marginLeft: 20
},
childViewStyle: {
margin: 20,
},
itemBlock: {
paddingVertical: 15,
},
itemImage: {
width: 50,
height: 50,
borderRadius: 25,
margin: 10
},
itemText: {
fontSize: 16,
justifyContent: 'center',
color: Colors.darkySkyBlue
},
itemName: {
fontSize: 20,
color: Colors.black,
},
separator: {
borderRadius: 4,
borderWidth: 1,
borderColor: Colors.lightGray,
},
pharmacyText: {
fontSize: 16,
fontFamily: Fonts.bold,
color: Colors.black
},
radioStyle: {
marginTop: 10,
marginRight: 50,
justifyContent: 'space-between'
},
arrowIconStyle: {
flexDirection: 'row',
justifyContent: 'space-between',
padding: 10
// flex: 1
}
});
with this code i am not able to select the radio button
Please let me know where it is going wrong as i am unable to select radio button in the flatlist separately.
i have changed onPress() like this
onPress( index) {
this.setState({ selected: index, });
}
and in renderItem method i have changed it like this
const { item, index } = data;
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>
Now the output is like this
Now the first radio button is selected but when i tried to select other radio buttons they are not getting selected.
Here is my mock data file which is used for Flatlist.
export const PHARMACY = [
{
key: 0,
name: 'Plaza Pharmacy',
type: 'Urgent Care Center - 4.01 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
{
key: 1,
name: 'Courtyard Pharmacy',
type: 'Urgent Care Center - 4.09 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 2,
name: 'Outptaient Pharmacy at the Cancer Center',
type: 'Urgent Care Center - 4.66 mi',
location: '3800 Gaylord Parkway, Ste 110\n FRISCO, TX 75034',
phone: '890.664.8130',
},
{
key: 3,
name: 'Carrolton Pharmacy',
type: 'Urgent Care Center - 4.08 mi',
location: '5236 West University Drive, Suite 1900\n MCKINNEY, TX 75071',
phone: '969.264.7190',
},
{
key: 4,
name: 'Garland Pharmacy',
type: 'Urgent Care Center - 22.11 mi',
location: '3417 Gaston Avenue, Suite 195\n Dallas, TX 75202',
phone: '469.764.7100',
},
];
I am exporting this const PHARMACY and importing it in my class and set this to a state variable "data".
you can do something like this in renderItem
renderItem = ({item, index}) => {
.....
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selectedIndex === index}
onPress={() => {this.onPress(index)}}
/>
}
and replace onPress with
onPress = (index) => {
this.setState({ selectedIndex: index });
}
and update FlatList with extraData props as FlatList needs to be re-rendered as
<FlatList
keyExtractor={this._keyExtractor}
data={this.state.data}
renderItem={this.renderItem}
ItemSeparatorComponent={this.renderSeparator}
extraData={this.state}
/>
You can refer in snack here
First, you need to get the index of the current item like so:
const { item, index } = data;
After you get the index of the current item you can then check if the current radio button is selected or not and for changing the radio button you need to pass the value of the current index to the onPress function.
<RadioButton
innerColor={Colors.darkBlue}
outerColor={Colors.lightGray}
animation={'bounceIn'}
isSelected={this.state.selected === index}
onPress={() => this.onPress(index)}
/>

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;

How to show 2 items in one pagination of react native snap carousel

How to dispaly 2 items for one pagination(first dot) and if we swipe then next 2 items should display with showing second dot active.
And if it is odd then last item should display my own component in react native snap carousel.
I would suggest that you go ahead and make the item you're rendering in the Carousel one that renders 2 things at once. The Carousel will paginate on whatever you pass to it, so if you're passing something with 2 items in it, it'll paginate on that, so for example:
<Carousel
layout="default"
data={arr}
renderItem={
({ item, index }) => (
<View style={styles.imageWrapper}>
<Image
style={styles.image}
source={item[0]}
resizeMode="cover"
accessibilityLabel="thumbnail"
/>
<Image
style={styles.image}
source={item[1]}
resizeMode="cover"
accessibilityLabel="thumbnail"
/>
</View>
)
}
lockScrollWhileSnapping={true} // Prevent the user from swiping again while the carousel is snapping to a position.
sliderWidth={screenWidth}
sliderHeight={screenWidth * 0.5}
itemWidth={screenWidth - 40}
activeSlideOffset={50}
enableSnap
onSnapToItem={onSnapToItem}
removeClippedSubviews={false}
firstItem={0}
contentContainerCustomStyle={styles.style}
/>
Create a function that split "entries" array into smaller arrays based on the size that you want
var slides = [];
const entriesSplitter = () => {
let size = 2; //Based on the size you want
while (entries.length > 0) {
slides.push(entries.splice(0, size));
}
};
then pass the slides array to <Carousel data={slides}/> then render each slide
in _renderItem
consider the following example:-
import React, { useState, useRef } from "react";
import { View,Text, Dimensions } from "react-native";
import Carousel, { Pagination } from "react-native-snap-carousel";
const { width: screenWidth, height: screenHeight } = Dimensions.get("window");
const myCarousel = () => {
const [activeSlide, setActiveSlide] = useState(0);
const carousel = useRef();
const entries = [
{
title: "Adidas"
},
{
title: "Nike"
},
{
title: "Puma"
},
{
title: "Reebok"
}
];
var slides = [];
const entriesSplitter = () => {
let size = 2; //Based on the size you want
while (entries.length > 0) {
slides.push(entries.splice(0, size));
}
};
// render every single slide
const _renderItem = ({ item,index }) => {
return (
<View style={{ flexDirection: "row", flexWrap: "wrap" }}>
{item.map(item => {
return <Text key={index}>{item.title}</Text>;
})}
</View>
);
};
return (
<View>
{entriesSplitter()}
<Carousel
ref={carousel}
data={slides}
renderItem={_renderItem}
onSnapToItem={index => setActiveSlide(index)}
sliderWidth={screenWidth}
sliderHeight={screenHeight}
itemWidth={screenWidth}
/>
<Pagination
dotsLength={2} // also based on number of sildes you want
activeDotIndex={activeSlide}
containerStyle={{ backgroundColor: "red", borderWidth: 2 }}
dotStyle={{
width: 10,
height: 10,
borderRadius: 5,
marginHorizontal: 8,
backgroundColor: "black"
}}
inactiveDotStyle={{
backgroundColor: "pink"
}}
inactiveDotOpacity={0.4}
inactiveDotScale={0.6}
/>
</View>
);
};
export default myCarousel;
This is how I implemented to show 3 carousels. We can customize it in many ways
import React from "react";
import { View, Dimensions, StyleSheet, Image } from "react-native";
import Carousel from "react-native-snap-carousel";
const windowWidth = Dimensions.get("window").width;
export default function MyCarousel() {
const images = [
{ id: 1, image: require("../assets/home-slider-1.jpg") },
{ id: 2, image: require("../assets/home-slider-2.jpg") },
{ id: 3, image: require("../assets/home-slider-3.jpg") },
{ id: 4, image: require("../assets/home-slider-4.jpg") }
];
// const imagesUri = [
// { id: 1, image: { uri: 'https://i.imgur.com/gG5Egof.jpg' } },
// { id: 2, image: { uri: 'https://i.imgur.com/gG5Egof.jpg' } },
// { id: 3, image: { uri: 'https://i.imgur.com/gG5Egof.jpg' } },
// { id: 4, image: { uri: 'https://i.imgur.com/gG5Egof.jpg' } }
// ];
const _renderItem = ({ item }) => {
return (
<View style={styles.slide}>
<Image
source={item.image}
style={styles.image}
// resizeMode="center"
></Image>
</View>
);
};
return (
<View style={styles.wrapper}>
<Carousel
data={images}
renderItem={_renderItem}
sliderWidth={windowWidth}
itemWidth={windowWidth - 70}
enableMomentum={false}
lockScrollWhileSnapping
autoplay
useScrollView
loop
autoplayInterval={3000}
/>
</View>
);
}
const styles = StyleSheet.create({
wrapper: {
height: 150
},
slide: {
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
},
image: {
flex: 1,
height: "100%",
width: "100%",
alignItems: "center",
justifyContent: "center"
}
});

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