Access array of objects to display in React Native Text component - javascript

I’m having trouble accessing an array of objects in my JSON data to display in a React Native Text component.
JSON data
{
"name": "Pizza Joint",
"when": [{
"day": ["Sat", "Sun"],
"start_time": "11:00",
"end_time": "23:00"
}]
}
Code
<View style={styles.container}>
<Text style={styles.name}>{venue.name}</Text>
<Text style={styles.time}>{venue.when[0].start_time}</Text>
</View>
This throws the error Undefined is not an object (evaluating 'venue.when') which I don't understand since console.log(type of venue.when) returns object.
How can I access the when object properties here?
Additional notes
I copied the app structure from this tutorial.
Here is VenueList.js:
'use strict';
var React = require('react-native');
var VenueDetail = require('./VenueDetail');
var {
Image,
StyleSheet,
Text,
View,
Component,
ListView,
TouchableHighlight,
ActivityIndicatorIOS
} = React;
var styles = StyleSheet.create({
container: {
flex: 1,
flexDirection: 'row',
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
padding: 10
},
thumbnail: {
width: 53,
height: 81,
marginRight: 10
},
rightContainer: {
flex: 1,
},
title: {
fontSize: 20,
marginBottom: 8
},
author: {
color: '#656565'
},
separator: {
height: 1,
backgroundColor: '#dddddd'
},
listView: {
backgroundColor: '#F5FCFF'
},
loading: {
flex: 1,
alignItems: 'center',
justifyContent: 'center'
}
});
// var REQUEST_URL = 'https://www.googleapis.com/books/v1/volumes?q=subject:fiction';
var REQUEST_URL = 'http://apib.miniguide.es/wall/today';
class VenueList extends Component {
render() {
if (this.state.isLoading) {
return this.renderLoadingView();
}
console.log(this.state.dataSource);
return (
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderVenue.bind(this)}
style={styles.listView}
/>
);
}
renderLoadingView() {
return (
<View style={styles.loading}>
<ActivityIndicatorIOS
size='large'/>
<Text>
Loading venues...
</Text>
</View>
);
}
constructor(props) {
super(props);
this.state = {
isLoading: true,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
componentDidMount() {
this.fetchData();
}
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData),
isLoading: false
});
})
.done();
}
renderVenue(venue) {
console.log(venue);
return (
<TouchableHighlight onPress={() => this.showVenueDetail(venue)} underlayColor='#dddddd'>
<View>
<View style={styles.container}>
<Image
// source={{uri: venue.images[0].url}}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title}>{venue.name}</Text>
<Text style={styles.author}>{venue.subtitle}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
module.exports = VenueList;
console.log(venue) produces the following format of data (I am simplifying output here for the purposes of the example):
{
name: 'Pizza Joint',
when: [{
day: ['Sat', 'Sun'],
start_time: '11:00',
end_time: '23:00'
}]
}
I notice the above is not JSON since the quotes on the keys have been stripped out.

Ok I figured it out. The problem was the JSON data set that our API produces has some documents with another definition that delimit the data (e.g. first is document that indicates featured). So React was throwing an error on the first document, which lacked the properties I was attempting to access.
To fix this, I added ternary expressions to the React components to check for the existence of properties:
renderVenue(venue) {
console.log(venue);
return (
<TouchableHighlight onPress={() => this.showVenueDetail(venue)} underlayColor='#dddddd'>
<View>
<View style={styles.container}>
<Image source={ 'images' in venue ? { uri: 'http://' + venue.images[0].url } : null}
style={styles.thumbnail} />
<View style={styles.rightContainer}>
<Text style={styles.title}>{venue.name}</Text>
<Text style={styles.author}>{venue.subtitle}</Text>
<Text style={styles.author}>{ 'when' in venue ? venue.when[0].start_date : null}</Text>
</View>
</View>
<View style={styles.separator} />
</View>
</TouchableHighlight>
);
}
}
Perhaps we should modify our API output.
In any event thanks for comments, they were helpful.

Related

FlatList component is not showing result

I am working on a task in which I have to implement infinite scroll paging of 600 dummy records to cards I have coded on FlatList render. So far I have implemented logic using FlatList but not successfully. Help is required where I am doing wrong thus not giving result. Here is the component code:
import React, { Component } from "react";
import { StyleSheet, ActivityIndicator, FlatList, View } from "react-native";
import { Card } from "react-native-elements";
class InfiniteScroll extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
data: [],
dummy: [],
fetchingStatus: false,
setOnLoad: false,
};
this.page = 0;
let onEndReached = false;
}
componentDidMount() {
var toput = [];
for (let i = 0; i < 598; i++) {
toput.push({
name: "KHAN MARKET",
order_no: "ORDER # DBZ-876",
status: "Order Completed",
price: "Total: $14.00",
date: "Dec 19, 2019 2:32 PM",
});
}
this.setState(
{
dummy: toput,
},
() => {
console.log(this.state.dummy);
}
);
this.apiCall();
}
apiCall = () => {
var that = this;
var old = that.page;
that.page = that.page + 10;
console.log(" *********** call " + this.page);
that.setState({ fetchingStatus: true });
that.setState({
data: [...this.state.data, ...this.state.dummy.slice(old, that.page)],
isLoading: false,
fetchingStatus: false,
setOnLoad: true,
});
};
BottomView = () => {
return (
<View>
{this.state.fetchingStatus ? (
<ActivityIndicator
size="large"
color="#F44336"
style={{ marginLeft: 6 }}
/>
) : null}
</View>
);
};
ItemSeparator = () => {
return (
<View
style={{
height: 0.5,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
};
render() {
return (
<View>
{this.state.isLoading ? (
<ActivityIndicator size="large" />
) : (
<FlatList
style={{ width: "100%" }}
keyExtractor={(item, index) => index}
data={this.state.data}
ItemSeparatorComponent={this.ItemSeparator}
onScrollEndDrag={() => console.log(" *********end")}
onScrollBeginDrag={() => console.log(" *******start")}
initialNumToRender={8}
maxToRenderPerBatch={2}
onEndReachedThreshold={0.1}
onMomentumScrollBegin={() => {
this.onEndReached = false;
}}
onEndReached={() => {
if (!this.onEndReached) {
this.apiCall(); // on end reached
this.onEndReached = true;
}
}}
renderItem={({ item, index }) => (
<View>
<Card>
<Text
style={{ justifyContent: "flex-start", fontWeight: "bold" }}
>
{item.name}
</Text>
<View style={styles.textview}>
<Text style={styles.orderno}>{item.order_no}</Text>
<Text style={styles.orderstatus}>{item.status}</Text>
</View>
<Text style={styles.amount}>{item.price}</Text>
<View style={styles.textview}>
<Text style={styles.orderno}>{item.date}</Text>
</View>
</Card>
</View>
)}
ListFooterComponent={this.BottomView}
/>
)}
</View>
);
}
}
const styles = StyleSheet.create({
textview: {
marginTop: 5,
flexDirection: "row",
justifyContent: "space-between",
},
orderno: {
flexDirection: "row",
justifyContent: "flex-start",
},
orderstatus: {
flexDirection: "row",
justifyContent: "flex-end",
color: "green",
},
ordercomplete: {
flexDirection: "row",
justifyContent: "flex-end",
color: "red",
},
amount: {
justifyContent: "flex-start",
marginTop: 5,
},
});
export default InfiniteScroll;
I will be very thankful if you can provide solution. Thanks
The thing here is that you are seting your dummy data and calling the apiCall next, the dummy data is not updated yet when you call the apiCall, ande the dummy data is shown as an empty array on the apiCall, you just ned to force some await before calling the function, just change from this:
this.setState(
{
dummy: toput,
},
() => {
console.log(this.state.dummy);
}
);
this.apiCall();
to this:
this.setState(
{
dummy: toput
},
() => {
this.apiCall();
}
);
Another thing I notice is that you are using some bad pratices like setting valuse direct on the class instead of using state and some messing with the scope (that = this), I've made some updates/suggestions in you code that may help with readability:
apiCall = () => {
const newPage = this.state.page + 10;
this.setState({
data: [
...this.state.data,
...this.state.dummy.slice(this.state.page, newPage)
],
isLoading: false,
fetchingStatus: false,
setOnLoad: true,
page: newPage
});
};
I've created a fully working example so you can see some more updates I've made in the code, that may help you as well:
https://codesandbox.io/s/stackoverflow-67166780-321ku?file=/src/App.js

React Native -- How to edit the value of dynamically generated TextInput fields?

I am trying to edit the value of the dynamically generated TextIput fields value.
Here is my JSON array
{
"measurements": [{
"id": 200,
"sub_type_id": 34,
"sub_type": "TOPS SL",
"measurement_input": "15"
}, {
"id": 201,
"sub_type_id": 47,
"sub_type": "TOPS ABOVE CHEST",
"measurement_input": "40"
}, {
"id": 202,
"sub_type_id": 48,
"sub_type": "TOPS CHEST",
"measurement_input": "42"
}]
}
base on the above JSON array data I have created TextInputs dynamically but the problem is I am not able to edit the TextInputs values.
Code for creating TextInput
....
this.state = {
...
subtypes: props.route.params.editdata.measurements,
inputData: [],
...
}
...
{this.state.subtypes.map((inputs, idx) => {
return (
<View style={{flex: 1, padding: 2, margin: 10}}>
<Text style={{margin: 2}}>{inputs.sub_type}</Text>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'center',
}}>
<View style={{flex: 1}}>
<TextInput
style={styles.textInput}
placeholder={inputs.sub_type}
value={inputs.measurement_input} //<------ value not able to change
onChangeText={(text) =>
this.addValues(text, idx, inputs.sub_type_id)
}
/>
</View>
</View>
</View>
);
})}
This is the function for taking value from TextInput.
//------ HERE I am not able to do editable parts --------
addValues = (text, index, id) => {
let dataArray = this.state.inputData;
let checkBool = false;
if (dataArray.length !== 0) {
dataArray.forEach((element) => {
if (element.index === index) {
element.measurement_input = text;
element.sub_type_id = id;
checkBool = true;
}
});
}
if (checkBool) {
this.setState({
inputData: dataArray,
});
} else {
dataArray.push({measurement_input: text, index: index, sub_type_id: id});
this.setState({
inputData: dataArray,
});
}
};
I tried many technics but not able to perform the value editable.
I fixed it using only two lines of code.
it doesn't need to copy the updated values in another array (inputData state array). Just only update to the same array (subtypes state array that contains my actual edited json data) and I used this.forceUpdate(); javascript function.
Here is the updated code, all the updated values will contain in the this.state.subtypes .
{this.state.subtypes.map((inputs, idx) => {
return (
<View style={{flex: 1, padding: 2, margin: 10}}>
<Text style={{margin: 2}}>{inputs.sub_type}</Text>
<View
style={{
flex: 1,
flexDirection: 'row',
alignItems: 'center',
}}>
<View style={{flex: 1}}>
<TextInput
style={styles.textInput}
placeholder={inputs.sub_type}
value={inputs.measurement_input}
onChangeText={(text) =>
{
//this.addValues(text, idx,inputs.sub_type_id, inputs.measurement_input) //<----- no need
// these two line of codes work out
this.state.subtypes[idx].measurement_input = text
this.forceUpdate();
}
}
/>
</View>
</View>
</View>
);
})}
Why are you using this.state.subtypes to render and set state on inputData?

TypeError:undefined is not an object evaluating this.state.items

I'm new to React native and development in general and am trying to retrieve data from an webservice and then render it but I seem to be running into different errors.
I am getting this error
import React, { Component } from "react";
import {StyleSheet,View,ActivityIndicator,FlatList,Text,TouchableOpacity} from "react-native";
export default class Source extends React.Component {
static navigationOptions = ({ navigation }) => {
return {
title: "Source Listing",
headerStyle: {backgroundColor: "#fff"},
headerTitleStyle: {textAlign: "center",flex: 1}
};
};
constructor(props) {
super(props);
this.state = {
loading: false,
items:[]
};
this.fetchRequest=this.fetchRequest.bind.this
}
FlatListItemSeparator = () => {
return (
<View style={{
height: .5,
width:"100%",
backgroundColor:"rgba(0,0,0,0.5)",
}}
/>
);
}
componentDidMount()
{
fetchRequest();
}
renderItem=(data)=>
<TouchableOpacity style={styles.list}>
<Text style={styles.lightText}>{data.item.name}</Text>
<Text style={styles.lightText}>{data.item.email}</Text>
<Text style={styles.lightText}>{data.item.company.name}</Text>
</TouchableOpacity>
render(){
fetchRequest()
{
const parseString = require('react-native-xml2js').parseString;
fetch('http://192.168.200.133/apptak_service/apptak.asmx/Get_Item_Master')
.then(response => response.text())
.then((response) => {
parseString(response, function (err, result) {
console.log(response)
});
}).catch((err) => {
console.log('fetch', err)
this.fetchdata();
})
if(this.state.loading){
return(
<View style={styles.loader}>
<ActivityIndicator size="large" color="#0c9"/>
</View>
)}}
return(
<View style={styles.container}>
<FlatList
data= {this.state.dataSource}
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem= {item=> this.renderItem(item)}
keyExtractor= {item=>item.id.toString()}
/>
</View>
)}
}
<FlatList
data={this.state.items}
renderItem={({ item }) => <Item title={item.title} />}
keyExtractor= {item=>item.id.toString()}
/>
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff"
},
loader:{
flex: 1,
justifyContent: "center",
alignItems: "center",
backgroundColor: "#fff"
},
list:{
paddingVertical: 4,
margin: 5,
backgroundColor: "#fff"
}
});
The main cause what i am trying to achieve is render the 'items' that are returned from the webservice.
Kindly guide me with this error

Flatlist does not render if have one object in data?

I have some issue with Flatlist, I have an array of objects I got them from DB,
and save them to state, now when I log the this.state.providers in render() method or in the callback func after setState I got the valid Object like this
[{username: "Test", key: "53HoDga6aYhHsV5pCi5sx6LGbx42"}]
but when I passed these object into data prop in <Flatlist data={this.state.providers} /> the flatlist not rendering!
but when I add the object in the data prop manual like this
<Flatlist data={[{username: "Test One", key: "53HoDga6aYhHsV5pCi5sx6LGbx42"}]}
the flatlist work very well,
But I'm sure the code is correct because I added them into other project and work very well!!
Edit
when I pass tow object into the array the flatlist work!!
so how to handle it if I got one object from DB!
Code
import React, { Component } from "react";
import firebase from "react-native-firebase";
import Icon from "react-native-vector-icons/Ionicons";
import {
View,
Text,
StyleSheet,
FlatList,
TouchableOpacity,
Dimensions
} from "react-native";
class ListChats extends Component {
constructor(props) {
super(props);
this.state = {
providers: []
};
}
_chatList = () => {
let currentUser = firebase.auth().currentUser.uid;
let ref = firebase.database().ref(`Messages/${currentUser}`);
let providersKeys = [];
ref.once("value").then(snapshot => {
snapshot.forEach(childsnap => {
console.log(childsnap.key);
providersKeys.push(childsnap.key);
});
let usernames = [];
providersKeys.forEach(key => {
firebase
.database()
.ref("users")
.child(key)
.once("value")
.then(providersShot => {
let username = providersShot.val().username;
usernames.push({ username: username, key: key });
});
});
this.setState({ providers: usernames }, () =>
console.log(this.state.providers)
);
});
};
componentDidMount() {
this._chatList();
}
_listEmptyComponent = () => {
return (
<View style={styles.container}>
<Text style={{ alignSelf: "center" }}>No Chats Found :O</Text>
</View>
);
};
render() {
console.log(this.state.providers); // I got [{username: "Test", key: "53HoDga6aYhHsV5pCi5sx6LGbx42"}]
return (
<View style={{ flex: 1 }}>
<FlatList
key={Math.random() * 1000}
data={this.state.providers}
contentContainerStyle={{ flexGrow: 1 }}
ListEmptyComponent={this._listEmptyComponent()}
keyExtractor={item => item.key.toString()}
renderItem={({ item }) => {
console.log("item", item);
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate("ChatDetails", {
Key: item.key,
providerName: item.username
})
}
>
<View style={styles.parent}>
<Icon name="ios-contact" size={50} color="#4d8dd6" />
<View
style={{
flex: 1,
justifyContent: "flex-start",
alignItems: "flex-start",
marginHorizontal: 25
}}
>
<Text
style={{
color: "#000",
fontSize: 17
}}
>
{item.username}
</Text>
</View>
<Icon name="ios-chatboxes" size={25} color="#d6d6d6" />
</View>
</TouchableOpacity>
);
}}
// keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
parent: {
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
paddingVertical: 25,
marginHorizontal: 15,
borderBottomWidth: 1,
borderBottomColor: "#eee"
}
});
export default ListChats;
You should add extraData property to FlatList:
extraData={this.state}
In here your using usernames.push method to add the data objects to array, using this method will not indicate the state about a need of a re-render even if the state is updated with a new array.
For this usual coding pattern is using the spread operator
this.setState({ providers: [...usernames] })
Use "extraData" props of FlatList.

How do I map over two arrays at the same time in react native?

How do I map over two different arrays in react native? In my case I'm fetching a response from server and mapping over it. Also there is another array named images which I want to list along with the fetched response from server.But the second mapping is looping within the first one. How do I separate it from the first?Following is my code.
sample code
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{images.map(b =>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
)}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
workers array is the json response I'm fetching from server.images array is as folows
export const images = [
{
image:'http://localhost:3000/Images/carpenter.png',
text:'hello'
},
{
image:'http://localhost:3000/Images/electrician.png',
text:'hii'
},
]
Also this how workers array looks like
updated
[
{
"sl.no": 1,
"worker_id": "wr_1",
"work_type": "carpenter",
"phone_num": "3456789243"
},
{
"sl.no": 2,
"worker_id": "wr_2",
"work_type": "electrician",
"phone_num": "345221344"
},
{
"sl.no": 3,
"worker_id": "wr_3",
"work_type": "plumber",
"phone_num": "8976545677"
}
]
You can simply move it above the first map and save the result:
render() {
const imagesToRender = images.map(b => {
return (
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
);
});
return (
<ScrollView>
{this.state.workers.map(a =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
{imagesToRender}
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
);
}
Also, don't forget to add key props to each Image and each CardSection.
you can easily use flatlist with better performance
import React, { Component } from "react";
import { View, FlatList, TouchableOpacity, Image, Text } from "react-native";
const workers = [
{ id: 1, name: 'Nima', images: [{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' },{ image: 'https://www.lens-rumors.com/wp-content/uploads/2014/10/Nikon-AF-S-DX-Nikkor-18-140mm-f3.5-5.6G-ED-VR-sample-images1.jpg', text: 'hello' }] },
{ id: 2, name: 'Mike', images: [{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },{ image: 'https://www.dike.lib.ia.us/images/sample-1.jpg/image', text: 'goodby' },] },
]
class Test extends Component {
constructor(props) {
super(props);
this.state = {
workers: workers
};
}
_renderItem = ({ item }) => {
console.log(item);
return (
<View style={{ flex: 1 }}>
<TouchableOpacity>
<View style={{ marginTop: 10, marginLeft: 120 }}>
{item.images.map((b, index) => {
console.log(b.image);
return (
<View key={index}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: b.image }}
/>
<Text
style={{ marginLeft: 20, fontSize: 20, color: "black" }}
>
{b.text}
</Text>
</View>
);
})}
<Text style={{ marginLeft: 20, fontSize: 20, color: "black" }}>
{item.name}
</Text>
</View>
</TouchableOpacity>
</View>
);
};
_keyExtractor = (item, index) => item.id.toString();
render() {
return (
<FlatList
data={this.state.workers}
extraData={this.state}
keyExtractor={this._keyExtractor}
renderItem={this._renderItem}
/>
);
}
}
export default Test;
If the images don't load, it's probably because you pass the wrong properties to the <Image /> component. Look up the docs for <Image /> component or replace it with <img /> instead and pass the url string of the image to the src attribute.
getImageUri(worker) {
// Decide which image to return for a certain type of worker
// For more workers and images, change the following logic
if(worker.work_type == 'carpenter') {
return images[0].image;
} else if(worker.work_type == 'electrician') {
return images[1].image;
} else {
return '';
}
}
render() {
...
<ScrollView>
{this.state.workers.map((a, index) =>
<CardSection>
<TouchableOpacity onPress={() => this.popupDialog.show()}>
<View style={{ marginTop: 10, marginLeft:120}}>
<Image
style={{ height: 100, width: 100 }}
source={{ uri: this.getImageUri(a)}}
/>
<Text style={{marginLeft:20, fontSize:20}}>{a.work_type}</Text>
</View>
</TouchableOpacity>
</CardSection>
)}
</ScrollView>
...
}

Categories

Resources