React Native Flat List - javascript

I am trying to use the React Native FlatList to loop thru a fetch API response for a news page that will load from a modal that contains a web view element. Everything seems to work fine, but every time I click the news article, it always loop thru all of them and give me the last article instead of the clicked article. If I console.log(item.title) it returns the clicked article title, but does not load it. I even passed the article data as a state but I am not sure what I am doing wrong, please help. Please ignore the extra code since I am focus on making sure it works properly before refactoring and removing the unused CSS. Here is the code. Let me know if you need a repo to post the link. Thanks in advance.
import React, { Component } from "react";
import { WebView } from "react-native-webview";
import {
View,
StyleSheet,
Text,
ActivityIndicator,
Image,
Button,
Linking,
TouchableOpacity,
Modal
} from "react-native";
import { FlatList } from "react-native-gesture-handler";
export default class NewsPage extends Component {
constructor(props) {
super(props);
this.state = {
dataSource: [],
isLoading: true,
modalVisible: false
};
}
componentDidMount() {
return fetch(
"https://newsapi.org/v2/everything?q=empleos&language=es&apiKey=bc9471b0c90c4d1a8d41860292e59d6b"
)
.then(response => response.json())
.then(responseJson => {
this.setState(
{
isLoading: false,
dataSource: responseJson.articles
},
function() {}
);
})
.catch(error => {
console.error(error);
});
}
setModalVisible(articleData) {
this.setState({
modalVisible: true,
modalArticleData: articleData
});
}
closeModal = () => {
this.setState({
modalVisible: false,
modalArticleData: {}
});
};
render() {
if (this.state.isLoading) {
return (
<View style={{ flex: 1, padding: 20 }}>
<ActivityIndicator />
</View>
);
}
return (
<View style={{ flex: 1, paddingTop: 20 }}>
<FlatList
data={this.state.dataSource}
keyExtractor={({ id }, index) => id}
renderItem={({ item }) => (
<View style={styles.newsListContainer}>
<Image
style={styles.newsImage}
source={{
uri:
item.urlToImage != null
? item.urlToImage
: "../assets/icon.png"
}}
/>
<View style={styles.newsInfo}>
<Text numberOfLines={2} style={styles.newsTitle}>
{item.title}
</Text>
<Text numberOfLines={3} style={styles.newsDescription}>
{item.description}
</Text>
<Text>{item.author}</Text>
</View>
<View style={styles.newsLink}>
<Button
title="Leer"
onPress={() => {
console.log(item);
this.setModalVisible(item);
}}
// {Linking.openURL(item.url);}}
/>
</View>
<Modal
animationType="slide"
transparent={false}
visible={this.state.modalVisible}
>
<View style={styles.newsHeader}>
<TouchableOpacity
onPress={() => {
this.closeModal();
}}
>
<Text style={styles.newsClose}>Cerrar</Text>
</TouchableOpacity>
<Text>{item.title}</Text>
</View>
<WebView
startInLoadingState={true}
source={{ uri: item.url }}
style={{ padding: 8 }}
/>
</Modal>
</View>
)}
/>
</View>
);
}
}
NewsPage.navigationOptions = {
headerTitle: "Noticias en EspaƱol"
};
const styles = StyleSheet.create({
mainBackgroundColor: {
backgroundColor: "#007bff",
padding: 8
},
mainBackground: {
alignItems: "center",
justifyContent: "center",
flex: 1
},
textHeader: {
backgroundColor: "#fff",
marginTop: 16,
fontSize: 32,
padding: 8,
borderRadius: 8,
color: "#007bff"
},
firstText: {
fontSize: 16,
marginTop: 16,
marginBottom: 0,
color: "#fff"
},
phone: {
marginTop: 16,
color: "#fff"
},
secondText: {
fontSize: 16,
marginTop: 0,
color: "#fff"
},
thirdText: {
fontSize: 16,
marginTop: 8,
color: "#ffffff",
textTransform: "uppercase",
textAlign: "center",
padding: 8,
marginBottom: 12
},
firstButton: {
marginBottom: 16
},
newsListContainer: {
width: "100%",
marginBottom: 4,
padding: 4,
flex: 1,
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between"
},
newsImage: {
flexBasis: "25%",
height: "100%"
},
newsInfo: {
flexBasis: "55%",
padding: 2,
alignSelf: "flex-start"
},
newsLink: {
flexBasis: "20%"
},
newsTitle: {
fontSize: 20,
color: "#000000",
marginLeft: 8
},
newsDescription: {
fontSize: 12,
color: "efefef",
marginLeft: 8
},
newsHeader: {
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
padding: 4
},
newsClose: {
fontSize: 20,
color: "red"
}
});

In your Modal, you are using
<Text>{item.title}</Text>
instead of
<Text>{this.state.modalArticleData.title}</Text>
also for the WebView component do the same.
source={{ uri: this.state.modalArticleData.url }}
One More Thing:
import Flatlist from react-native instead of
react-native-gesture-handler

Related

Is there a way to use mutable arrays in FlatList?

I'm trying to make a toy stopwatch app in order to learn react-native.
I made a lap system, but it is getting way too slow when there are >15 laps. I think the poor performance point is the laps: this.state.laps.concat([d - this.state.lapTimerStart]) part, because of .concat is making a new object every time the Lap button is pressed.
I've heard that .push is way faster than .concat.
So I tried to use .push, but because .push was mutating the array, and FlatList was a PureComponent so it re-rendered only when the props have changed.
I found a way, but it was just the same as doing .concat because basically, it was
let lapArr = this.state.laps;
Array.prototype.push.apply(lapArr, [d - this.state.lapTimerStart]);
this.setState({
laps: lapArr,
})
The full code is
import React, {Component} from 'react';
import {
View,
Text,
StyleSheet,
TouchableHighlight,
FlatList,
} from 'react-native';
import {useTheme} from 'react-native-paper';
import TimeFormatter from 'minutes-seconds-milliseconds';
class Stopwatch extends Component {
constructor(props) {
super(props);
this.state = {
laps: [],
isRunning: false,
mainTimer: null,
lapTimer: null,
mainTimerStart: null,
lapTimerStart: null,
};
}
handleLapReset() {
let {isRunning, mainTimerStart, lapTimer} = this.state;
if (mainTimerStart) {
if (isRunning) {
const d = new Date();
this.setState({
lapTimerStart: d,
lapTimer: d - this.state.lapTimerStart + lapTimer,
laps: this.state.laps.concat([d - this.state.lapTimerStart]),
});
return;
}
this.state.laps = [];
this.setState({
mainTimerStart: null,
lapTimerStart: null,
mainTimer: 0,
lapTimer: 0,
});
}
}
handleStartStop() {
let {isRunning, mainTimer, lapTimer} = this.state;
if (isRunning) {
clearInterval(this.interval);
this.setState({
isRunning: false,
});
return;
}
const d = new Date();
this.setState({
mainTimerStart: d,
lapTimerStart: d,
isRunning: true,
});
this.interval = setInterval(() => {
const t = new Date();
this.setState({
mainTimer: t - this.state.mainTimerStart + mainTimer,
lapTimer: t - this.state.lapTimerStart + lapTimer,
});
}, 10);
}
_renderTimers() {
const {theme} = this.props;
return (
<View
style={[
styles.timerWrapper,
{backgroundColor: theme.colors.background},
]}>
<View style={styles.timerWrapperInner}>
<Text style={[styles.lapTimer, {color: theme.colors.text}]}>
{TimeFormatter(this.state.lapTimer)}
</Text>
<Text style={[styles.mainTimer, {color: theme.colors.text}]}>
{TimeFormatter(this.state.mainTimer)}
</Text>
</View>
</View>
);
}
_renderButtons() {
const {theme} = this.props;
return (
<View style={styles.buttonWrapper}>
<TouchableHighlight
underlayColor={theme.colors.disabled}
onPress={this.handleLapReset.bind(this)}
style={[styles.button, {backgroundColor: theme.colors.background}]}>
<Text style={[styles.lapResetBtn, {color: theme.colors.text}]}>
{this.state.mainTimerStart && !this.state.isRunning
? 'Reset'
: 'Lap'}
</Text>
</TouchableHighlight>
<TouchableHighlight
underlayColor={theme.colors.disabled}
onPress={this.handleStartStop.bind(this)}
style={[styles.button, {backgroundColor: theme.colors.background}]}>
<Text
style={[styles.startBtn, this.state.isRunning && styles.stopBtn]}>
{this.state.isRunning ? 'Stop' : 'Start'}
</Text>
</TouchableHighlight>
</View>
);
}
_renderLaps() {
return (
<View style={styles.lapsWrapper}>
<FlatList
data={this.state.laps}
renderItem={this.renderItem}
keyExtractor={this.keyExtractor}
/>
</View>
);
}
keyExtractor(item, index) {
return index.toString();
}
renderItem({item, index}) {
return (
<View style={styles.lapRow}>
<View style={styles.lapStyle}>
<View style={styles.lapBoxStyle} />
<Text style={styles.lapNumber}>{index + 1}</Text>
</View>
<View style={styles.lapStyle}>
<Text style={styles.lapTime}>{TimeFormatter(item)}</Text>
</View>
</View>
);
}
render() {
return (
<View style={styles.container}>
<View style={styles.top}>{this._renderTimers()}</View>
<View style={styles.middle}>{this._renderButtons()}</View>
<View style={styles.bottom}>{this._renderLaps()}</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {flex: 1},
timerWrapper: {
justifyContent: 'center',
flex: 1,
},
top: {
flex: 1,
},
middle: {
flex: 1,
backgroundColor: '#F0EFF5',
},
bottom: {
flex: 2,
},
mainTimer: {
fontSize: 50,
fontFamily: 'CircularStd-Medium',
alignSelf: 'center',
},
lapTimer: {
fontSize: 18,
fontFamily: 'CircularStd-Medium',
alignSelf: 'flex-end',
},
timerWrapperInner: {
alignSelf: 'center',
},
buttonWrapper: {
flexDirection: 'row',
justifyContent: 'space-around',
paddingTop: 15,
paddingBottom: 30,
},
button: {
height: 80,
width: 80,
borderRadius: 40,
backgroundColor: '#FFF',
justifyContent: 'center',
alignItems: 'center',
},
lapRow: {
flexDirection: 'row',
justifyContent: 'space-between',
height: 40,
paddingTop: 10,
},
lapNumber: {
flexDirection: 'row',
fontSize: 16,
fontFamily: 'CircularStd-Book',
color: '#777',
flex: 1,
},
lapTime: {
flexDirection: 'row',
color: '#000',
fontSize: 20,
fontFamily: 'CircularStd-Book',
flex: 1,
},
startBtn: {
color: '#0C0',
fontFamily: 'CircularStd-Book',
},
stopBtn: {
color: '#C00',
fontFamily: 'CircularStd-Book',
},
lapsWrapper: {
backgroundColor: '#ddd',
},
lapResetBtn: {
fontFamily: 'CircularStd-Book',
},
lapStyle: {
width: '40%',
flexDirection: 'row',
},
lapBoxStyle: {
flexDirection: 'row',
flex: 1,
},
});
export default function StopwatchScreen(props) {
const theme = useTheme();
return <Stopwatch {...props} theme={theme} />;
}
I tried not to use arrow functions, many news, but it didn't help that much.
FlatList is a pure component and it is mandatory to give new ref of data prop to render list . You should use concat but it created new object.
i think the main issue is renderItem.
create a separate PureComponent to avoid re-rendeing of items
class Item extends PureComponent {
const {item,index} = this.props;
return (
<View style={styles.lapRow}>
<View style={styles.lapStyle}>
<View style={styles.lapBoxStyle} />
<Text style={styles.lapNumber}>{index + 1}</Text>
</View>
<View style={styles.lapStyle}>
<Text style={styles.lapTime}>{TimeFormatter(item)}</Text>
</View>
</View>
);
}
and use in render item
renderItem({item, index}) {
return (
<Item item={item} index={index} />
);
}

React Native Component Exception <Text strings must be rendered within a Text component>

I am trying to build a react native application with the expo, First I try to build this application with my chrome web browser, it was worked without any issue after that I try to test the application with my android device and I'm getting an exception - "Text strings must be rendered within a <Text> component" HomeScreen.js files. I have no idea why this happened. My code as follows,
/*This is an Example of Grid View in React Native*/
// import * as React from "react";
import React from 'react';
import { Image, FlatList, StyleSheet, View, Text, TouchableOpacity, TextInput } from 'react-native';
import { COLORS } from '../../asserts/Colors/Colors';
import { CATEGORIES } from '../../asserts/mocks/itemListData';
import CategoryGridTitle from '../components/HomeGridTile';
import { HeaderButtons, Item } from 'react-navigation-header-buttons';
import { HeaderButton } from '../components/HeaderButton';
import HomeScreenButton from '../components/HomeScreenButton';
//import all the components we will need
const renderTopBarItems = (topBarItems) => {
return (
<TouchableOpacity
style={styles.topBar}>
<Text style={styles.textStyle}> {topBarItems.item.category} </Text>
</TouchableOpacity>
)
}
const HomeScreen = props => {
const renderGridItem = (itemData) => {
return <CategoryGridTitle
title={itemData.item.title}
image={itemData.item.image}
onSelect={() => {
props.navigation.navigate({
routeName: 'PaymentHandlerScreen',
params: {
categoryId: itemData.item.id
}
});
}} />;
}
// const [images, setImages] = React.useState(picsumImages);
return (
<View style={styles.mainBody}>
<View style={styles.searchContainer}>
<TextInput
placeholder='Search'
style={styles.formField}
placeholderTextColor={'#888888'}
/>
<TouchableOpacity onPress={() => props.navigation.navigate('BarCodeScannerScreen')}
style={styles.saveFormField}>
<Image
source={require('../../../images/barcode.png')}
style={{
width: '100%',
height: '30%',
resizeMode: 'contain',
alignContent: 'center',
}}
/> </TouchableOpacity>
</View>
<View style={styles.tabBar}>
<FlatList
horizontal
pagingEnabled={true}
showsHorizontalScrollIndicator={false}
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderTopBarItems} />
</View>
<FlatList
keyExtractor={(item, index) => item.id}
data={CATEGORIES}
renderItem={renderGridItem}
numColumns={3} />
<HomeScreenButton style={styles.buttonView} />
</View>
);
};
HomeScreen.navigationOptions = navigationData => {
return {
headerTitle: 'Tickets',
headerRight: (
<HeaderButtons HeaderButtonComponent={HeaderButton}>
<Item
title='profile'
iconName='ios-star'
onPress={() => {
console.log('profile clicked');
}} />
<Item
title='more'
iconName='md-more'
onPress={() => {
console.log('more clicked');
}} />
</HeaderButtons>
)
};
};
export default HomeScreen;
const styles = StyleSheet.create({
mainBody: {
flex: 1,
justifyContent: 'center',
backgroundColor: COLORS.background,
paddingTop: '3%',
},
searchContainer: {
flex: 1,
flexDirection: 'row',
},
tabBar: {
paddingBottom: '3%',
},
topBar: {
width: 150,
borderWidth: 1,
borderRadius: 20,
borderColor: COLORS.primary_blue,
padding: '5%',
marginLeft: '5%',
},
textStyle: {
color: COLORS.primary_blue,
textAlign: 'center',
fontWeight: 'bold',
fontSize: 14,
},
formField: {
flex: 4,
borderWidth: 1,
padding: '4%',
marginLeft: '2%',
borderRadius: 10,
borderColor: COLORS.gray,
backgroundColor: COLORS.gray,
fontSize: 15,
height: '35%',
},
saveFormField: {
flex: 0.5,
justifyContent: 'space-between',
alignItems: 'center',
margin: 10,
},
buttonView: {
position: 'absolute',
bottom: 0,
left: 0,
},
});
Thank you.
I ran into this error a couple of times. RN doesn't like extra spaces in tags. try removing the spaces before and after {topBarItems.item.category}
<Text style={styles.textStyle}>{topBarItems.item.category}</Text>

I can't re-render the FlatList after empty data?

I Get the data from firebase real-time and put it I FlatList and when I delete if from Console "Firebase" it's deleted from the List in screen very well but the last item in the Array "Data" couldn't be deleted I don't know why!
I use an onRefresh Props but not help me because we all know the DB is real-time and when we will add any item it's will be in the last without refresh it So it's not work with the last item too and just the loading stuck without re-render the FlatList
Although I use .once('value') when I get data from DB, refresh work but when I refresh after deleting the last item the loading refresh stuck and can't disappear the last item
so how can I solve this issue?
here is my code
import auth from '#react-native-firebase/auth';
import database from '#react-native-firebase/database';
import React, {Component} from 'react';
import {
Dimensions,
FlatList,
Image,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
const {width} = Dimensions.get('window');
export default class PendingOrders extends Component {
constructor(props) {
super(props);
this.state = {
orders: [],
forceUpdate: true,
isFetching: false,
};
}
onRefresh = () => {
this.setState({isFetching: true}, () => this.getApiData());
};
getApiData = () => {
try {
const uid = auth().currentUser.uid;
const Orders = database().ref(`usersOrders/${uid}`);
Orders.on('value', snapshot => {
let orders = [];
snapshot.forEach(childSnapshot => {
if (childSnapshot.val().status == 'pending') {
orders.push({
buildingNumber: childSnapshot.val().buildingNumber,
service: childSnapshot.val().categoryName,
date: childSnapshot.val().date,
time: childSnapshot.val().time,
description: childSnapshot.val().problemDescription,
status: childSnapshot.val().status,
images: childSnapshot.val().Images,
});
this.setState({orders, forceUpdate: false, isFetching: false}, () =>
console.log(this.state.orders),
);
return;
}
});
});
} catch (err) {
console.log('Error fetching data: ', err);
}
};
componentDidMount() {
this.getApiData();
}
_listEmptyComponent = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
style={{
width,
height: width * 0.7,
resizeMode: 'contain',
}}
source={require('../../assets/empty.png')}
/>
<Text
style={{
color: '#000',
marginVertical: 15,
textAlign: 'center',
fontSize: 20,
}}>
No item found
</Text>
</View>
);
};
render() {
console.log('is?', this.state.forceUpdate);
return (
<FlatList
showsVerticalScrollIndicator={false}
data={this.state.orders}
extraData={this.state.isFetching}
onRefresh={() => this.onRefresh()}
ListEmptyComponent={this._listEmptyComponent()}
refreshing={this.state.isFetching}
contentContainerStyle={{
flexBasis: '100%',
}}
renderItem={({item}) => {
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('OrderDetailsScreen', {
service: item.service,
time: item.time,
date: item.date,
description: item.description,
images: item.images,
status: item.status,
})
}
style={{
margin: 15,
borderRadius: 10,
borderWidth: 1,
flexDirection: 'row',
borderColor: '#ddd',
}}>
<Image
style={{
borderRadius: 10,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
width: 150,
height: 150,
}}
resizeMode="cover"
source={item.images[0]}
/>
<View
style={{
margin: 5,
marginLeft: 10,
justifyContent: 'space-evenly',
}}>
<Text
style={{
marginBottom: 5,
fontWeight: 'bold',
fontSize: 16,
marginTop: 5,
}}>
{item.service}
</Text>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
// paddingHorizontal: 5,
}}>
<Icon name="date-range" color="#AEACAC" size={20} />
<Text style={{paddingHorizontal: 5}}>{item.date}</Text>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 5,
}}>
<Icon name="access-time" color="#AEACAC" size={20} />
<Text style={{paddingHorizontal: 5}}>{item.time}</Text>
</View>
</View>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{marginBottom: 5, width: 160, marginTop: 5}}>
{item.description}
</Text>
</View>
</TouchableOpacity>
);
}}
keyExtractor={(item, index) => index.toString()}
/>
);
}
}
The key for re activating flatlist is extraData. just change it's value to something else and it will trigger flatlist re rendering. for simplicity pass a boolean value to extraData. every time you want to re active the flatlist, just toggle the value of extraData

Increment and Decrement quantity in ListView using react-native

I am running sample project. I want to display the increment and decrement values. In my project increment and decrement values are not displayed. How to change the values in text.
Here is my code:
constructor(props){
super(props);
this.state={
increment:0,
decrement:1
}
}
incrementFunc(){
var countIncrement = this.state.increment
this.setState({
increment : countIncrement + 1
},()=>{
alert(this.state.increment)
})
}
decrementFunc(){
var countDecrement = this.state.increment
this.setState({
decrement : countDecrement- 1,
},()=>{
alert(this.state.decrement)
})
}
<View style={styles.arrowsView}>
<TouchableOpacity onPress ={this.incrementFunc.bind(this)}>
<Image style={styles.arrowStyle} source={require('#images/up-arrow.png')} />
</TouchableOpacity>
</View>
<View style={styles.numberView}>
<Text style={{fontSize:15, color:'black'}}>{this.state.increment}
</Text>
</View>
<View style={styles.arrowsView}>
<TouchableOpacity onPress ={this.decrementFunc.bind(this)}>
<Image style={styles.arrowStyle} source={require('#images/down-arrow.png')} />
</TouchableOpacity>
</View>
here is my screenshot:
Please give any suggestion. Thank You
Here is the solution which i had made in my project , what i did
Firstly ->created an another component which will help us to have a quantity input functionality inside list element
Secondly ->This component needs to be called by the parent component inside list renderRow to make this component visible in all the rows.
and put this QuantityInput component class inside the same file where you want to use it...
Thirdly -> Run yours application and have a look like this...
Have a look to the file attached...
/*Use this component in yours parent view inside list to have a Quantity Input inside list , I have used native base as well if some thing gets miss please have native base in yours project...*/
{<QuantityInput item={this.state.data[rowID]} viewAddToCart={false} />}
/* QuantityInput */
class QuantityInput extends React.Component {
userProductQty = Array.apply(null, Array(this.props.item.length)).map(Number.prototype.valueOf, 1);
itemPosition = -1;
constructor(props) {
super(props);
this.state = {
viewAddToCart: this.props.viewAddToCart,
item: this.props.item,
style: { flex: 1 },
styleTextInput: { backgroundColor: '#ffffff' },
styleButton: { backgroundColor: '#000000' },
styleImage: { width: 12, height: 12 },
editable: true,
stepsize: 1,
initialValue: 1,
min: 1,
max: 100
}
}
upBtnPressed = (dataSource, fieldName) => {
if (dataSource.userQty < this.state.max) {
let value = (parseInt(dataSource.userQty) + parseInt(this.state.stepsize)).toString();
dataSource.userQty = value;
this.setState({
item: dataSource
});
}
}
downBtnPressed = (dataSource, fieldName, props) => {
if (dataSource.userQty < this.state.min) {
let value = (parseInt(dataSource.userQty) - parseInt(this.state.stepsize)).toString();
dataSource.userQty = value;
this.setState({
item: dataSource
});
}
}
onChangeText = (text, item, fieldName, itemPosition) => {
if (!isNaN(text)) {
item.userQty = text.toString();
this.setState({
item: item
});
} else {
item.userQty = 0;
this.setState({
item: item
});
}
}
render() {
return (
this.state.viewAddToCart ?
/* Add to cart */
<View style={[{ flexDirection: 'row' }]}>
<Left style={{ flex: 0 }}>
<Button rounded danger
title="Add"
color="white"
style={[{ width: 70 }, { height: 30 }, { justifyContent: 'center' }]}
onPress={() => { this.props.onAddToCart(this.state.item); this.setState({ viewAddToCart: false }) }}
>
<Text style={[{ color: 'white' }]}>{string.addToCart}</Text>
</Button>
{/*</TouchableOpacity>*/}
</Left>
</View>
:
/* Quantity Text */
<View style={{ flexDirection: 'row' }}>
<View style={stylesQuantityText.verticle}>
<TouchableOpacity style={[styles.button, styles.transparentBkg, { backgroundColor: '#d9534f' }, { paddingLeft: 10 }, { borderTopLeftRadius: 5 }, { borderBottomLeftRadius: 5 }, { justifyContent: 'center' }, { alignItems: 'center' }]} onPress={() => { this.downBtnPressed(this.state.item, this.state.item.productId); }} >
<Text style={[{ width: 20 }, { height: 26 }, { fontSize: 21 }, { alignSelf: 'center' }, { paddingBottom: 10 }, { fontWeight: 'bold' }]}>-</Text>
{/*<Icon name={"remove"} />*/}
</TouchableOpacity>
<TextInput
style={[stylesQuantityText.textinput, this.state.styleTextInput]}
editable={this.state.editable}
keyboardType={'numeric'}
text={this.state.item.userQty.toString()}
value={this.state.item.userQty.toString()}
ref={"Qty" + this.state.item.productId}
key={"Qty" + this.state.item.productId}
onChangeText={(text) => { this.onChangeText(text, this.state.item, this.state.item.productId); this.props.onAddToCart(this.state.item); }} />
<TouchableOpacity style={[styles.button, styles.transparentBkg, { backgroundColor: '#d9534f' }, { paddingLeft: 10 }, { borderTopRightRadius: 5 }, { borderBottomRightRadius: 5 }]} onPress={() => { this.upBtnPressed(this.state.item, this.state.item.productId); this.props.onAddToCart(this.props.item); }}>
{/*<Icon name={"add"}
size={27} />*/}
<Text style={[{ width: 20 }, { height: 26 }, { fontSize: 19 }, { alignSelf: 'center' }]}>+</Text>
</TouchableOpacity>
</View>
</View>
);
}
}
const stylesQuantityText = StyleSheet.create({
wrapper: {
flex: 1,
backgroundColor: '#eeeeee'
},
verticle: {
flexDirection: 'row',
paddingLeft: 0,
paddingRight: 0,
},
horizontal: {
flexDirection: 'row'
},
textinput: {
backgroundColor: '#eeeeee',
textAlign: 'center',
width: 30,
borderColor: 'black',
borderWidth: 1,
height: 26
},
button: {
backgroundColor: '#dedede',
padding: 5
},
image: {
width: 18,
height: 18
},
buttonText: {
alignSelf: 'center'
}
});
]1

Firebase.push failed: first argument contains an invalid key($$typeof)

I am learning react native for the first time for developing android app not IOS one and I was learning from Building a Simple ToDo App With React Native and Firebase tutorial from youtube.But I am getting an error while trying to add items. I get following error
Firebase.push failed: first argument contains an invalid key($$typeof) in property 'restaurant.restaurant_name.targetInst._nativeParent._currentElement'.keys must be non-empty strings and cant contain ".","#","$","/","["
What might be creating this issue?
import React, { Component } from 'react';
import Firebase from 'firebase';
import {
AppRegistry,
StyleSheet,
Text,
View,
TouchableHighlight,
TextInput,
ListView
} from 'react-native';
class foodOrderSystem extends Component {
constructor(props){
super(props);
const firebaseRef = new Firebase('foodordersystem.firebaseio.com');
console.log(firebaseRef);
this.itemRef = firebaseRef.child('restaurant');
this.state = {
newRestaurant:'',
// estimated_delivery:'',
// description:'',
restaurantSource: new ListView.DataSource({rowHasChanged: (row1,row2) => row1 !== row2})
};
this.restaurant = [ ];
}
componentDidMount() {
this.itemRef.on('child_added', (snap) => {
this.restaurant.push({
id:snap.key(),
text:snap.val()
});
this.setState({
restaurantSource:this.state.restaurantSource.cloneWithRows(this.restaurant)
});
});
this.itemRef.on('child_removed', (snap) => {
this.restaurant = this.restaurant.filter((x) => x.id !== snap.key());
this.setState({
restaurantSource:this.state.restaurantSource.cloneWithRows(this.restaurant)
});
});
}
addRestaurant(){
if(this.state.newRestaurant !== ''){
this.itemRef.push({
restaurant_name: this.state.newRestaurant
});
this.setState({ newRestaurant:''});
}
}
removeRestaurant(rowData){
this.itemRef.child(rowData.id).remove();
}
render() {
return (
<View style={styles.appContainer}>
<View style={styles.titleView}>
<Text style={styles.titleText}>
foodOrderSystem
</Text>
</View>
<View style={styles.inputcontainer}>
<TextInput
style={styles.input}
onChange={(text) => this.setState({newRestaurant:text})}
value={this.state.newRestaurant} />
<TouchableHighlight
style={styles.button}
onPress={ () => this.addRestaurant() }
underlayColor="#00ffff">
<Text style={styles.btnText}>Add</Text>
</TouchableHighlight>
</View>
<ListView
dataSource={this.state.restaurantSource}
renderRow={this.renderRow.bind(this)} />
</View>
);
}
renderRow(rowData){
return(
<TouchableHighlight
underlayColor="#dddddd"
onPress={() => this.removeRestaurant(rowData)}>
<View>
<View style={styles.row}>
<Text style={styles.todoText}>{rowData.text}</Text>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
const styles = StyleSheet.create({
appContainer:{
flex: 1
},
titleView:{
backgroundColor: '#48afdb',
paddingTop: 30,
paddingBottom: 10,
flexDirection: 'row'
},
titleText:{
color: '#fff',
textAlign: 'center',
fontWeight: 'bold',
flex: 1,
fontSize: 20,
},
inputcontainer: {
marginTop: 5,
padding: 10,
flexDirection: 'row'
},
button: {
height: 36,
flex: 2,
flexDirection: 'row',
backgroundColor: '#48afdb',
justifyContent: 'center',
color: '#FFFFFF',
borderRadius: 4,
},
btnText: {
fontSize: 18,
color: '#fff',
marginTop: 6,
},
input: {
height: 36,
padding: 4,
marginRight: 5,
flex: 4,
fontSize: 18,
borderWidth: 1,
borderColor: '#48afdb',
borderRadius: 4,
color: '#48BBEC'
},
row: {
flexDirection: 'row',
padding: 12,
height: 44
},
separator: {
height: 1,
backgroundColor: '#CCCCCC',
},
todoText: {
flex: 1,
},
});
AppRegistry.registerComponent('foodOrderSystem', () => foodOrderSystem);
Here is the attachment of error

Categories