React Native list view data parsing from JSON - react-native

This is the JSON im trying to parse and show in my list view.
The data I would like to show on my list view is ZoneInfo["Name"] as a section header. For the list view, there would be 3 text showing the Name,QueueTime or ShowTime.
I have my JSON saved in my state variable.
This is the code I've been trying to retrieve the data from the JSON.
{this.state.loading? <Spinner /> : <List dataArray={this.state.results.items} renderRow={(item) =>
<ListItem>
<Text>{item}</Text>
</ListItem>
} />}
Anyone can guide me on how I can parse the JSON and show it on my listview?

There are for sure multiple ways of achieving this, up till now I'm pretty happy using it this way:
I'm sure you will find your way around ;-)
export default class extends React.Component {
constructor(props) {
super(props);
this.renderRow = this.renderRow.bind(this);
this.renderSectionHeader = this.renderSectionHeader.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2,
sectionHeaderHasChanged: (s1, s2) => s1 !== s2});
this.state = {
dataSource: ds.cloneWithRowsAndSections({}),
};
}
componentDidMount(){
... load your json and assign data to the state
this.setState({
dataSource: this.state.dataSource.cloneWithRowsAndSections(spots)
});
}
renderRow(rowData: string, sectionID: number, rowID: number) {
return (
<TouchableOpacity onPress={()=>this.onRowPress(rowData)}>
... your row content
</TouchableOpacity>
)
}
renderSectionHeader(sectionData, category) {
return (
<View style={styles.rowHeaderContainer}>
... your header content
</View>
)
}
render(){
return (
<View style={styles.container}>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderRow}
enableEmptySections={true}
renderSectionHeader={this.renderSectionHeader}
/>
</View>
);
}
}

Related

Get user input from input field in react similar to getElementById in react native using props

I am doing a loan calculation app and i run into the trouble since i am new to react native and previously i have been manipulating the DOM using querySelector or getElementById functions. However this does not work in react, and i am using state to store the value from the user, but i just can't seem to get it right, What am i doing wrong?
I've inserted the calculation element that is later rendered in app.js. All elements are showing up with no error, but the problem is to get user input data and then be able to use that data and do calculations.
Here is my Class
class LanKalkylElement extends React.Component {
constructor(props) {
super(props);
this.state = {
loanAmount: 20000,
loanInterest: 2.5,
loanYear: 10,
};
}
changeAmount(loanAmount) {
this.setState(() => {
return {
loanAmount: parseFloat(loanAmount),
};
});
}
changeInterest(loanInterest) {
this.setState(() => {
return {
loanInterest: parseFloat(loanInterest),
};
});
}
changeYear(loanYear) {
this.setState(() => {
return {
loanYear: parseFloat(loanYear),
};
});
}
calcButton() {
Alert.alert(this.props.loanAmount);
}
buttonHomeFunc() {
this.props.navigation.navigate('Start');
}
render() {
const {loanAmount, loanInterest, loanYear} = this.state;
return(
<View style={styles.contentStyle}>
<Text style={styles.text}> Lånebelopp </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanAmount}
onValueChange={this.changeAmount.bind(this)} />
<Text style={styles.text}> Ränta </Text>
<TextInput style={styles.numericInput}
onBlur={Keyboard.dismiss}
keyboardType={'numeric'}
value={loanInterest}
onValueChange={this.changeInterest.bind(this)} />
<Text style={styles.text}> Antal år: {String(loanYear)}</Text>
<Slider step={1}
maximumValue={15}
value={loanYear}
onValueChange={this.changeYear.bind(this)} />
<Button title='Kalkylera' onPress={() => this.calcButton()}/>
<Text style={styles.textResult}>Total summa att återbetala:</Text>
<Text style={styles.textResult}>varav räntekostnad:</Text>
<Button title='Tillbaka' onPress={() => this.buttonHomeFunc()}/>
</View>
)
}
}
export default withNavigation(LanKalkylElement);
When a user changes a value in a text input, onValueChange is called. You have bound this prop to functions that modify the state for this component.
This means the value in the text input will always match the value in the state. Therefore, if you need to access the value in a text input you would simply retrieve it from the state, like this:
const loanAmount = this.state.loanAmount;
doSomethingWithLoanAmount(loanAmount);

How to implement a collapsible box in react native?

I am trying to implement a collapsible box in react native.Its working fine for dummy data. But when i tried to list the data response from server i'm getting error.I'm using map method over the response for listing the details.But showing error evaluating this.state.details.map.Also i'm confused to where to place the map method.Below is the code that i've tried.I refer this doc for collapsible box.
Example
class DetailedView extends Component{
constructor(props){
super(props);
this.icons = {
'up' : require('../Images/Arrowhead.png'),
'down' : require('../Images/Arrowhead-Down.png')
};
this.state = {
title : props.title,
expanded : true,
animation : new Animated.Value()
};
}
toggle(){
let initialValue = this.state.expanded? this.state.maxHeight + this.state.minHeight : this.state.minHeight,
finalValue = this.state.expanded? this.state.minHeight : this.state.maxHeight + this.state.minHeight;
this.setState({
expanded : !this.state.expanded
});
this.state.animation.setValue(initialValue);
Animated.spring(
this.state.animation,
{
toValue: finalValue
}
).start();
}
_setMaxHeight(event){
this.setState({
maxHeight : event.nativeEvent.layout.height
});
}
_setMinHeight(event){
this.setState({
minHeight : event.nativeEvent.layout.height
});
}
state = {details: []};
componentWillMount(){
fetch('https://www.mywebsite.com' + this.props.navigation.state.params.id )
.then((response) => response.json())
.then((responseData) =>
this.setState({
details:responseData
})
);
}
render(){
let icon = this.icons['down'];
if(this.state.expanded){
icon = this.icons['up'];
}
return this.state.details.map(detail =>
<Animated.View
style={[styles.container,{height: this.state.animation}]}>
{detail.data.curriculum.map(curr =>
<View onLayout={this._setMinHeight.bind(this)}>
<Card>
<CardSection>
<View style={styles.thumbnailContainerStyle}>
<Text style={styles.userStyle}>
Hii
</Text>
</View>
<TouchableHighlight onPress={this.toggle.bind(this)}
underlayColor="#f1f1f1">
<Image style={styles.buttonImage} source={icon}></Image>
</TouchableHighlight>
</CardSection>
</Card>
</View>
<View style={styles.body} onLayout={this._setMaxHeight.bind(this)}>
{this.props.children}
<Card>
<CardSection>
<Text>{this.props.navigation.state.params.id}</Text>
</CardSection>
</Card>
</View>
)}
</Animated.View>
);
}
}
This is the screenshot for working code with dummy data
1. Solving the Error :
The API call you are making is asynchronous and once the API is called, the code continues to execute before getting the response from the API. The component tries to map through this.state.details before there are any details.
A solution here is that you need to set an ActicityIndicator/Loader initially when component is mounted and once you get the details/response from the API, the state changes and then you can map through this.state.details
Add empty details array to your initial state.
state = { details:[] }
Then put your return this.state.details.map(detail.... Inside an if condition like this
if(this.state.details.length > 0) {
<map here>
} else {
return <ActivityLoader />
}
2. Where to place the map methiod
You need to put it inside a function and call that function from within you render method.
showDetailsFunction() {
return this.state.details.map(detail =>
}
render() {
return(
{this.showDetailsFunction()}
)
}

React Native - how to use map function on an object

I'm creating an APP that get some data from fetch function. No problem here. The array has the data correctly. I'm doing it like this:
constructor(){
super()
this.state = {
fetching: false,
api: []
}
}
componentWillMount(){
this.setState({ fetching: true })
api.getMonuments().then(res => {
this.setState({
api: res,
fetching: false
})
})
}
I got this data: an array of 4 objects
Then I want to pass that data to another scene. I'm doing it like this:
<View style={styles.contentContainer}>
<TouchableHighlight
style={[styles.button, {marginBottom:0}]}
onPress={() => navigate('Monumento', this.state.api)}
underlayColor='#000'
>
<View style={styles.buttonContent}>
<Animatable.Text
style={styles.buttonText}
animation="bounceInLeft"
duration={1500}
>
Click here!
</Animatable.Text>
</View>
</TouchableHighlight>
</View>
On the other scene I get that data with the navigation.state.params but the problem now is that there is no more an array with 4 objects in it, but instead there is an object that have 4 objects in it...if I console log the data that is what's appears
render(){
const api = this.props.navigation.state.params;
console.log('API:', api)
...
Now I want to use the map function but I can't because 'api' is not a function...How can I workaround this?
render(){
var api={"bar":"nihao"};
return(
<View>
{Object.entries(api).map(([key,v])=>{
return <View key={key}><Text>{v}</Text></View>
})}
</View>
)
}
api is a single object not array.
api is a array.
render(){
var api=[{"bar":"nihao"},{"bar":"nihao2"},{"bar":"nihao3"}];
return(
<View>
{api.map((v,index)=>{
return <View key={index}><Text>{v.bar}</Text></View>
})}
</View>
)
}
You can use Object.entries with RN for mapping the key/value pairs of an object. Eg:
const api = { 'foo': 'bar', 'foz': 'baz'};
...
render() {
return (
Object.entries(api).map(([key, value]) => {
return <View key={key}>{value}</View>
});
)
}
The issue is you are accessing params object, but what you want is api array. I guess you are using react navigation. If so, then your call to navigate function should be like this:
navigate('Monumento', {api: this.state.api}).
And you can retrieve it like this:
this.props.navigation.state.params.api.
Navigate function takes screen name and params object.
Read this: https://reactnavigation.org/docs/navigators/navigation-prop#navigate-Link-to-other-screens

show message if listview is empty react native

If List View is empty, I would like to show the header(Which is happening) and also show a alternate Text saying "List is Empty".
I tried doing getRowCount(), which returns 0. But how can I insert the text in the same view.
`
<Image source={require('./img/background.png')} style={GlobalStyles.bgImageContainer}>
<ListView style={{margin: 5}}
initialListSize={10}
dataSource={this.state.familydataSource}
renderRow={this.renderRow}
renderSeparator={::this._renderSeparator}
renderSectionHeader={this.renderFamilySectionHeader}
enableEmptySections={true}
/>
<ListView style={{margin: 5}}
dataSource={this.state.friendsdataSource}
renderRow={this.renderRow}
renderSeparator={::this._renderSeparator}
renderSectionHeader={this.renderFriendsSectionHeader}
enableEmptySections={true}
/>
</Image>
`
I initially misunderstood what you meant by header, thinking it was a component separate from the ListView. To show a separate message in addition to the ListView (instead of as a replacement to it), I would use the flex style to determine whether the ListView should take up the full height or just a percentage. In that later case you can render your message below the ListView so that both appear.
You can separate the rendering of the ListView and message into two functions like so:
_renderMessage() {
return (
<View style={{ flex:0.5 }}>
<Text>List is Empty</Text>
</View>
)
}
render() {
const listViewProportion = this.state.dataSource.getRowCount() == 0 ? 0.5 : 1
return (
<View style={{ flex:1 }}>
<ListView
style={{ flex: listViewProportion }}
...
/>
{if (listViewProportion != 1) {
this._renderMessage()
}}
</View>
)
}
Maybe this could help in order to give you an idea for what you want. Have you tried something like this?
constructor(props) {
super(props);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([]),
}
}
renderIf(condition){
if(condition){
return (<ListView
dataSource={this.state.dataSource}
renderRow={(rowData) => <Text>{rowData}</Text>} />)
} else{
return (<Text> There is no data </Text>)
}
}
componentDidMount(){
var datos = ['John', 'Joel', 'James', 'Jimmy', 'Jackson', 'Jillian', 'Julie', 'Devin']
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({dataSource: ds.cloneWithRows(datos)})
}
render(){
return(
{this.renderIf(this.state.dataSource.getRowCount())}
);
}
In this example, I imagine that we start with 0 elements in the array. So the constructor runs first, and then I declare my dataSource has 0 elements. After this, render methods is executed. As there are 0 elements, when we call to renderIf it will return the second part of the if statement. Once render method is finished, then componentDidMount method will be called to and in this method we will do all the operations in order to retrieve our data from our server o whatever. Once this method ends, our dataSource will have information, so it will render the ListView with all our information.
In the ListViewDataSource there is a property _cachedRowCount which holds a number with total rows of a ListView
Here is an example of how you could handle an empty ListView message:
render() {
return (
<View style={styles.listViewContainer}>
{this.state.dataSource._cachedRowCount > 0 // condition
? // if true
<ListView
dataSource={this.state.dataSource}
renderRow={this._renderRow.bind(this)}
>
</ListView>
: // if false
<Text>Nothing found</Text>
}
</View>
)
}

Raw text "" must be wrapped in explicit text component

I am using a react native component listview to render nested items as follows:
constructor() {
super();
this.renderRow = this.renderRow.bind(this);
const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.state = {
dataSource: ds.cloneWithRows([
[
{'main':'q', 'sub': 'y'}
],
[
{'main':'x', 'sub': 'f'},
{'main':'c', 'sub': 'b'}
]
]),
};
}
renderRow(rowData, section, row) {
const total = this.state.dataSource.getRowCount();
let rowView = rowData.map( x => {
return <View> <Text>{x['main']}</Text> <Text>{x['sub']}</Text> </View>
})
return (
<View>
{rowView}
</View>
);
}
render() {
return (
<View style={styles.container}>
<ListView style={styles.listView}
dataSource={this.state.dataSource}
renderRow={this.renderRow}
/>
</View>
);
}
But I am getting following error:
Raw text "" must be wrapped in explicit text component.
I am unable to track where I am getting this error from.
let rowView = rowData.map( x => {
return <View> <Text>{x['main']}</Text> <Text>{x['sub']}</Text> </View>
})
Remove spaces between View And Text Components. Use tab and enter instead of space character.
let rowView = rowData.map( x => {
return <View>
<Text>{x['main']}</Text>
<Text>{x['sub']}</Text>
</View>
})
To solve this, I used regex search & replace in entire file, and replacing >(\s)+<Text with ><Text works.
Mainly this issue arise due to space between <View> <Text> etc
A trick fix for this issue could be execute Format Document (I am using VScode (IDE), you can try with any IDE which have code styling capability). By Style fixing spaces between tags gets automatically removed.