I'm making a list view were I will view a list of some data from my database. But after running the program all I got is white background screen. Does anyone knows the solution?
screen shot
Here is my code
export default class Pasta extends Component {
constructor() {
super()
this.state = {
dataSource: []
}
}
renderItem = ({ item }) => {
return (
<View style = {{flex: 1, flexDirection: 'row'}}>
<View style = {{flex: 1, justifyContent: 'center'}}>
<Text>
{item.menu_desc}
</Text>
<Text>
{item.menu_price}
</Text>
</View>
</View>
)
}
componentDidMount() {
const url = 'http://192.***.***.***:9090/menu'
fetch(url)
.then((response) => response.json())
.then((responseJson) => {
this.setState({
dataSource: responseJson.menu
})
})
}
render() {
return (
<View style = { styles.container }>
<FlatList
data = { this.state.dataSource }
renderItem = {this.renderItem}
/>
</View>
);
}
}
Add extraData prop to your FlatList to cause a re-render
keyExtractor = (item, index) => item.id; // note: id is the unique key for each item
render() {
return (
<FlatList
data = {this.state.dataSource}
renderItem = {this.renderItem}
extraData={this.state}
keyExtractor={this.keyExtractor}
/>
);
}
Also log and verify your data is present. I suggest referring to FlatList docs for more props like keyExtractor etc.
Related
I try to fetch data from mySQL based on the 'Type' selected on Picker and update the ListView with the fetched data. So, I specify everything when the Picker's onValueChange will call the componentDidMount() function to fetch new data and update the ListView.
The problem is, when I select TypeA in Picker, the data in the ListView does not update, but when I select TypeB in Picker for the second time, the ListView updated based on TypeA. Then, I select TypeC, the ListView updated based on TypeB.
Is there is a problem with my code or I using the wrong method to do so?
export default class ProfileScreen extends Component {
constructor(props) {
super(props);
this.state = {
TypeInput : 'Vegetarian',
isLoading: true,
};
}
static navigationOptions = {
title: 'View/Edit',
};
OpenSecondActivity(id) {
this.props.navigation.navigate('Second', { ListViewClickItemHolder: id });
}
componentDidMount() {
return fetch('https://unsurfaced-cross.000webhostapp.com/getRecipeRN.php',{
method: 'POST',
headers: {
'Accept': 'application/json',
'Content-Type': 'application/json',
},
body: JSON.stringify({
RecipeType: this.state.TypeInput
})
}).then((response) => response.json())
.then((responseJson) => {
// Fetch Data update the List View Content
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
isLoading: false,
dataSource: ds.cloneWithRows(responseJson),
});
})
.catch((error) => {
console.error(error);
});
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
render() {
const { navigate } = this.props.navigation;
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.container}>
<Text style={styles.text}>Recipe Type: </Text>
<Picker
style={{width: 200}}
selectedValue={this.state.TypeInput}
onValueChange={
(itemValue, itemIndex) => {
this.setState({TypeInput: itemValue, isLoading:true})
this.componentDidMount()
this.forceUpdate()
}
}>
<Picker.Item label="Vegetarian" value="Vegetarian" />
<Picker.Item label="Fast Food" value="Fast Food" />
<Picker.Item label="Healthy" value="Healthy" />
<Picker.Item label="No-Cook" value="No-Cook" />
<Picker.Item label="Make Ahead" value="Make Ahead" />
</Picker>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) => <Text style={styles.rowViewContainer}
onPress={this.OpenSecondActivity.bind(this, rowData.RecipeID)}> {rowData.RecipeName} </Text>}
/>
</View>
)
}
}
You are manually calling componentDidMount() which I think is wrong practice.
According to docs
componentDidMount() is invoked immediately after a component is mounted. Initialization that requires DOM nodes should go here. If you need to load data from a remote endpoint, this is a good place to instantiate the network request.
In your case what I would suggest is when you are changing the value in picker, have some different handler function which will take care of updating the state. You should not manually call componentDidMount(), as it is one of the lifecycle hooks of the component.
More on Component Lifecycle.
Above code now seems to be fine, in your code, once you receive the updated data, you can update the datasource of the ListView as below,
...
fetch('https://unsurfaced-cross.000webhostapp.com/createRN.php', {
...
.then((responseJson) => {
this.setState({dataSource: responseJson})
}
...
And you ListView, should be something like this
<ListView
automaticallyAdjustContentInsets={false}
dataSource={ds.cloneWithRows(this.state.dataSource)}
renderRow={(rowData)=> {
return <View>
<Text style={rowStyle.label} content={rowData.someField} />
</View>
}}
...
/>
Seem like the better way to deal with this issue is playing around with componentDidUpdate() instead of componentDidMount()
Can try refer to my completed application here below.
Github: https://github.com/slnn3r/RecipeRedux.git
The Project above:
is using Redux Architecture
using PHP mySQL database
using Stack Navigation
Hello fellow programmers, I am having this problem developing this React-Native app where i am rendering a ListView of 'Services' where in each row it has a Text and a Switch, and I am able to render it but when i tap on the row's switch to change the value it goest back to its initial value real fast, I was wondering how to keep this change of vale but since I am new into this I am pretty clueless of how this is done: so far I have the ListView component where I call my ListItem component, heres my code;
class ListView extends Component {
constructor(props) {
super(props);
this.state = {
servicios: []
};
}
componentDidMount() {
AsyncStorage.getItem("token").then((value) => {
axios.get('http://MYURL/api/servicio/index?token=' + value)
.then(response => this.setState({ servicios: response.data.servicios }))
.catch(function (error) {
console.log(error);
});
}).done();
}
renderList() {
console.log('here');
return this.state.servicios.map(servicio =>
<ListItem key={servicio.id} servicio={servicio} />);
}
render() {
const { navigation } = this.props.navigation;
return (
<ScrollView>
{this.renderList()}
</ScrollView>
);
}
}
ListItem.js
const ListItem = ({ servicio }) => {
const { nombre, created_at, estatus } = servicio;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle, headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Text style={headerTextStyle}>{nombre}</Text>
</View>
<View style={headerContentStyle}>
<Switch value={estatus}/>
</View>
</CardSection>
</Card>
);
export default ListItem;
I missed the styles to not make this post too long, I may have the clue that i've got to put the current's row switch status in the State but I dont know how to do it, I would be really glad if you guys could help me?
Thanks in advance.
In order to change value of the switch you need to change value in the state from which you're rendering the ListView. I haven't tested that and wrote that from the top of my head, but you should achieve it by introducing small changes here and there:
ListItem.js
const ListItem = ({ servicio, onToggleSwitch }) => {
const { nombre, created_at, estatus, id } = servicio;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle, headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Text style={headerTextStyle}>{nombre}</Text>
</View>
<View style={headerContentStyle}>
<Switch value={estatus} onValueChange={(value) => onToggleSwitch(id, value)} />
</View>
</CardSection>
</Card>
);
export default ListItem;
ListView.js
class ListView extends Component {
constructor(props) {
super(props);
this.state = {
servicios: []
};
}
onToggleSwitch = (id, value) => {
const servicios = [...this.state.servicios]
const index = servicios.findIndex(item => item.id === id)
servicios[index].estatus = value
this.setState({ servicios })
}
componentDidMount() {
AsyncStorage.getItem("token").then((value) => {
axios.get('http://MYURL/api/servicio/index?token=' + value)
.then(response => this.setState({ servicios: response.data.servicios }))
.catch(function (error) {
console.log(error);
});
}).done();
}
renderList() {
console.log('here');
return this.state.servicios.map(servicio =>
<ListItem key={servicio.id} servicio={servicio} onToggleSwitch={this.onToggleSwitch} />);
}
render() {
const { navigation } = this.props.navigation;
return (
<ScrollView>
{this.renderList()}
</ScrollView>
);
}
}
I can't display json data in listview. I get json data in console.log but not in listview isLoading is always on false.
I dont get any errors .catch(error => console.warn("error")).
Result on screen is first View because this.state.isLoading is false.
Here is a code:
import React, { Component } from 'react';
import { AppRegistry, StyleSheet, ListView, Text, View,Image,TouchableHighlight } from 'react-native';
var productArray = [];
class ListViewDemo extends Component {
constructor(props) {
console.warn("constructor");
super(props);
var dataSource = new ListView.DataSource({rowHasChanged:(r1,r2) => r1.guid != r2.guid});
this.state = {
dataSource: dataSource.cloneWithRows(productArray),
isLoading:true
}
}
componentDidMount() {
console.warn("componentDidMount");
this.getTheData(function(json){
productArray = json;
console.warn(productArray);
this.setState = ({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
}.bind(this));
console.warn("component -> " + this.state.isLoading);
}
getTheData(callback) {
console.warn("callback");
var url = "https://raw.githubusercontent.com/darkarmyIN/React-Native-DynamicListView/master/appledata.json";
fetch(url)
.then(response => response.json())
.then(json => callback(json))
.catch(error => console.warn("error"));
}
renderRow(rowData, sectionID, rowID) {
console.warn("renderRow");
return (
<TouchableHighlight underlayColor='#dddddd' style={{height:44}}>
<View>
<Text style={{fontSize: 20, color: '#000000'}} numberOfLines={1}>{rowData.display_string}</Text>
<Text style={{fontSize: 20, color: '#000000'}} numberOfLines={1}>test</Text>
<View style={{height: 1, backgroundColor: '#dddddd'}}/>
</View>
</TouchableHighlight>
);
}
render() {
console.warn("render" + this.state.isLoading);
var currentView = (this.state.isLoading) ? <View style={{height: 110, backgroundColor: '#dddddd'}} /> : <ListView dataSource={this.state.dataSource} renderRow={this.renderRow.bind(this)} enableEmptySections={true}/>
return(
<View>
{currentView}
</View>
);
}
}
// App registration and rendering
AppRegistry.registerComponent('AwesomeProject', () => ListViewDemo);
I see a couple of mistakes here.
In your componentDidMount, you are setting datasource intead of dataSource:
componentDidMount() {
console.warn("componentDidMount");
this.getTheData(function(json){
productArray = json;
console.warn(productArray);
this.setState = ({
//datasource:this.state.dataSource.cloneWithRows(productArray),
dataSource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
}.bind(this));
console.warn("component -> " + this.state.isLoading);
}
That's why you're not being able to render, because dataSource is never populated. It is just a little spelling mistake.
You are probably not getting into the second then in your getTheData method because you are not returning a Promise:
getTheData(callback) {
console.warn("callback");
var url = "https://raw.githubusercontent.com/darkarmyIN/React-Native-DynamicListView/master/appledata.json";
fetch(url)
//.then(response => response.json())
.then(response => return response.json())
.then(json => callback(json))
.catch(error => console.warn("error"));
}
Your are making a mistake with your setState, your are assigning it instead of calling it:
//this.setState = ({
// datasource:this.state.dataSource.cloneWithRows(productArray),
// isLoading:false
//})
this.setState({
dataSource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
Let me know if it works.
You are setting the function setState instead of calling it
this.setState = ({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
should be
this.setState({
datasource:this.state.dataSource.cloneWithRows(productArray),
isLoading:false
})
I know how to add and remove a single component by changing the state. But this way wont work if you have multiple components to remove. For instance lets say I have 3 Views. How can I remove them when I click on them.
Example code:
class Example extends Component {
render(){
return (
<View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 2</Text>
</TouchAbleOpacity>
</View>
<View>
<TouchAbleOpacity onPress={() => this.removeView()}>
<Text>Remove View 3</Text>
</TouchAbleOpacity>
</View>
</View>
)
}
removeView(){
}
}
Another example will be when I have a ListView with buttons inside. These are buttons to invite a user. When I click on the button I want to hide the button for that specific row in the ListView.
Any suggestions?
Thanks to Giorgos I found a solution for my own question. I created a separate component with a hide function inside the component. Now I can just add this component anywhere in a view or in a listView and when I click on it it will hide. Remember this only hides the component and does not unmount it.
This is just an example so I created a button component.
My Button Component:
class ButtonComponent extends React.Component {
constructor(props) {
super(props);
this.state = {
hide:false
}
}
render() {
return (
<View style={styles.container}>
{this.renderButtonComponent()}
</View>
);
}
renderButtonComponent(){
if(!this.state.hide){
return (
<TouchableOpacity onPress={this.hide.bind(this)}>
<Text>Button</Text>
</TouchableOpacity>
);
}
}
hide(){
this.setState({
hide:true
});
}
}
In my View I just render my Component:
render() {
return (
<View style={styles.container}>
<ButtonComponent/>
<ButtonComponent/>
<ButtonComponent/>
</View>
);
}
You have to use your component's state. Whenever you call setState the component's render() function is triggered again. There based on what the current state is, you can decide what to show and what not. For example:
class Example extends Component {
constructor(props){
// initialize state
this.state = {
isView1Visible: true,
isView2Visible: true,
isView2Visible: true
}
}
render(){
return (
<View>
{ this.renderView1() }
{ this.renderView2() }
{ this.renderView3() }
</View>
)
}
renderView1(){
if(this.state.isView1Visible){
return (
<View>
<TouchAbleOpacity onPress={() => this.setState( {isView1Visible: false} )}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
)
}
renderView2(){
if(this.state.isView2Visible){
return (
...
)
}
renderView3(){
if(this.state.isView3Visible){
return (
...
)
}
}
In the above example, you render your view based on the current state. When the button is clicked, you update the state by calling setState() which, like I mentioned before, will trigger another call to render().
With the ListView the approach is the same but the implementation slightly different. What you need to do there is to save your list of items in the state of your component and whenever you want to add/remove an item, you update the list accordingly and then update the state using setState. For example, something similar to this:
constructor(props) {
super(props);
var list = [ ... ]
const ds = new ListView.DataSource({ rowHasChanged: (r1, r2) => r1 !== r2 });
this.state = {
dataSource: ds,
items: ds.cloneWithRows(list)
};
}
render() {
return (
<View>
<ListView
dataSource={this.state.items}
renderRow={(rowData) => this.renderRow(rowData) /> } />
</View>
)
}
renderRow(rowData) {
<View>
<TouchAbleOpacity onPress={() => this.updateList()}>
<Text>Remove View 1</Text>
</TouchAbleOpacity>
</View>
}
updateList() {
// do some changes to your list and update the state.
var newItems = ...
this.setState({
items: newItems
})
}
Hope this helps.
I'm currently learning react-native and getting stuck in ListView problems. I want to search data in ListView from a TextInput and i expect that the result is in ListView too.
Here's what i've done so far:
var PageOne = React.createClass({
getInitialState:function(){
return{
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2,
}),
loaded: false,
colorProps: {
titleColor: 'white',
},
searchText:"",
}
},
componentDidMount() {
this.fetchData();
},
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
loaded: true,
});
})
.done();
},
setSearchText(action){
let searchText = event.nativeEvent.text;
this.setState({searchText});
/*
*
*
*/
},
render() {
return (
<View style={{ flex: 1 }}>
<ToolbarAndroid
title="Movies"
{...this.state.colorProps}
style={{height:40, backgroundColor:'blue'}}
/>
<TextInput
placeholder="Search movies......."
value={this.state.searchText}
onChange={this.setSearchText.bind(this)} />
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderMovie}
style={styles.listView}
/>
</View>
);
},
renderMovie(movie) {
return (
<TouchableOpacity onPress={this._handlePressList.bind(this, movie)}>
<View style={styles.container}>
<Image
source={{uri: movie.posters.thumbnail}}
style={styles.thumbnail}
/>
<View style={styles.rightContainer}>
<Text style={styles.title}>{movie.title}</Text>
<Text style={styles.year}>{movie.year}</Text>
</View>
</View>
</TouchableOpacity>
);
},
what am i supposed to do next? Please help. Thanks :)
Update! After read the answer from urbancvek, i add function in setSearchText() method like this:
setSearchText(event){
const searchText = event.nativeEvent.text;
moviesLength = this.state.movies.length;
aMovie = this.state.movies;
const filteredMovies = this.state.movies.filter(checkTitle);
console.log("movies: " + JSON.stringify(filteredMovies));
function checkTitle() {
for(i=0;i<moviesLength;i++){
if(aMovie[i].title === searchText){
console.log("found: " + aMovie[i].title);
return aMovie[i];
}
}
}
this.setState({
searchText,
dataSource: this.state.dataSource.cloneWithRows(filteredMovies),
});
},
But it always show me all the movies, not filtered one. any ideas? thanks
In your fetchData method you should probably save responseData to state too. You will then interact with this data each time search field changes.
fetchData() {
fetch(REQUEST_URL)
.then((response) => response.json())
.then((responseData) => {
this.setState({
dataSource: this.state.dataSource.cloneWithRows(responseData.movies),
movies: responseData.movies,
loaded: true,
});
}).done();
},
Now in your setSearchText() method you should include some filter function that will find the movies you want from the movies you saved to state in fetchData().
setSearchText(action){
const searchText = event.nativeEvent.text;
const filteredMovies = this.state.movies.filter(/* awesome filter function */);
this.setState({
searchText,
dataSource: this.state.dataSource.cloneWithRows(filteredMovies);
});
},
Each time you want to update ListView you have to update it's dataSource. Only this way ListView component can realize that the data it's displaying has changed.
Hope I helped.
Searching data in the listview is basically just searching for it in a linked list or an array just take the input and search for it in the datasource or data blob. You can use linear search or binary search whichever you prefer.
The UIExplorer example from Facebook shows how to do this with ListView:
https://github.com/facebook/react-native/tree/master/Examples/UIExplorer