I am building an app in react native and I am using Lisview to display some data for some strange reason the endReached is triggered itself without me scrolling the listview and the listView ends up displaying all items at first like I have incremented page value each time, also i get duplicate results for first api call with value page 1.
Code:
import React, {Component} from 'react';
import {Alert, ListView, Text, View} from 'react-native';
import categoryApi from './category.api';
import styles from './category.styles';
import CategoryItem from './category.items.component';
import ShopsNear from "../listshops/list-shops.component";
export default class Category extends React.Component {
constructor(props) {
super(props);
this.state = {
rawData: [],
isLoading: false,
categories: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
}),
page: 1,
};
}
static navigationOptions = {
headerTitle: 'Categories',
title: 'Categories',
};
componentDidMount() {
this.fetchCategories();
}
fetchCategories() {
this.setState({isLoading: true});
categoryApi.getOne(this.state.page).then(response => {
if (response.data) {
this.setState({
rawData: this.state.rawData.concat(response.data.data),
categories: this.state.categories.cloneWithRows(this.state.rawData.concat(response.data.data)),
isLoading: false,
});
} else {
this.setState({isLoading: false});
Alert.alert(
'Something wrong happened!',
'My Alert Msg',
[],
{cancelable: true}
)
}
});
}
componentWillMount() {
}
showMore = () => {
this.setState({page: this.state.page + 1});
console.log("End reached... page: " + this.state.page);
this.fetchCategories();
};
render() {
const {navigate} = this.props.navigation;
return (
<View style={styles.container}>
<View style={styles.projektiHeader}>
<Text style={styles.projekti}>VALITSE PROJEKTI</Text>
</View>
<View style={styles.categoriesList}>
<ListView
dataSource={this.state.categories}
renderRow={(rowData) => <CategoryItem navigate={navigate} item={rowData}/>}
renderSeparator={(sectionId, rowId) => <View key={rowId} style={styles.separator}/>}
onEndReached={this.showMore}
/>
</View>
<View style={styles.shopsNear}>
<ShopsNear navigate={navigate}/>
</View>
</View>
);
}
}
Basically showMore() is itself, Anyone knows what's happening here?
I am trying to achieve so everytime I scroll and reaches the end of listview to call the showMore function which will fetch data from an API.
check the onEndReachedThreshold props for listView component.
Related
I'm trying to display a flat list (values form json placeholder) filterable with a search bar and it's not rendering for some reason. The values are not visible. Thanks!
The flat list code:
import React, { Component } from "react";
import { View, Text, FlatList, Button } from "react-native";
import { ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
temp: [],
error: null,
search: null
};
}
componentDidMount() {
this.getData();
}
getData = async () => {
const url = `https://jsonplaceholder.typicode.com/users`;
this.setState({ loading: true });
try {
const response = await fetch(url);
const json = await response.json();
this.setResult(json);
} catch (e) {
this.setState({ error: 'Error Loading content', loading: false });
}
};
setResult = (res) => {
this.setState({
data: [...this.state.data, ...res],
temp: [...this.state.temp, ...res],
error: res.error || null,
loading: false
});
}
renderHeader = () => {
return <SearchBar placeholder="Search Here..."
lightTheme round editable={true}
value={this.state.search}
onChangeText={this.updateSearch} />;
};
updateSearch = search => {
this.setState({ search }, () => {
if ('' == search) {
this.setState({
data: [...this.state.temp]
});
return;
}
this.state.data = this.state.temp.filter(function(item){
return item.name.includes(search);
}).map(function({id, name, email}){
return {id, name, email};
});
});
};
render() {
return (
this.state.error != null ?
<View style={{flexDirection: 'column',justifyContent: 'center', alignItems: 'center' }}>
<Text>{this.state.error}</Text>
<Button onPress={
() => {
this.getData();
}
} title="Reload" />
</View> :
<FlatList
ListHeaderComponent={this.renderHeader}
data={this.state.data}
keyExtractor={item => item.email}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name}`}
subtitle={item.email}
/>
)}
/>
);
}
}
export default FlatListDemo;
Importing this list to:
import React, {useState, useEffect} from 'react'
import { Text, View, StyleSheet, StatusBar, SafeAreaView } from "react-native"
import "firebase/auth";
import 'react-native-gesture-handler';
import "firebase/auth";
import 'firebase/compat/auth';
import 'firebase/compat/firestore';
import FlatListDemo from './FlatListDemo';
const Home: () => React$Node = () => {
return(
<>
<StatusBar barStyle="dark-content"/>
<SafeAreaView style={{flex: 1}}>
<FlatListDemo/>
</SafeAreaView>
</>
);
};
export default Home;
This is what it looks like (values should be there). Using Expo on Android:
photo1
Thanks! I appreciate the help!
It seems to me that you are using a ListElement as it is defined in version 1.2.0 of react-native-elements but the actual version that you are using is > 1.2.0.
You are implementing it similar to the documentation of react-native-elements 1.2.0.
However, the definition of ListItem has changed in newer version. In the newest version, the ListItem component is just a simple view wrapper. Hence, it needs to contain children in order to display the information that you want.
In your case this could be implemented as follows (I did not know what roundAvatar is doing).
renderItem={({ item }) => (
<ListItem>
<ListItem.Content>
<ListItem.Title>{`${item.name}`}</ListItem.Title>
<ListItem.Subtitle>{item.email}</ListItem.Subtitle>
</ListItem.Content>
</ListItem>
Check the documentation of the newest version for the exact features that you want to use.
I'm trying to implement the react-navigation package to make one screen link to another. I'm getting an error "undefined is not an object (evaluating '_this2.props.navigation.navigate') - React Native"
I'm fairly sure this error is because I have nothing in the App.js part of my project.
I have a file called screen.js that has two classes (one for each screen), and an onpress function that calls the this.props.navigator to change screens.
Screen.js
import React, { Component } from 'react';
import {
StyleSheet,
Text,
TextInput,
View,
TouchableHighlight,
PanResponder,
AppRegistry,
Image,
Alert,
} from 'react-native';
import { Card, Button, Icon } from 'react-native-elements';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { Constants, MapView } from 'expo';
class HomeScreen extends Component {
static navigationOptions = {
title: 'Home',
};
constructor() {
super();
this.state = {...};
}
_handleButtonPress = () => {
Alert.alert('Button pressed!', 'You did it!');
};
componentWillMount() {
this.panResponderRef = PanResponder.create({
onStartShouldSetPanResponder: () => true,
onMoveShouldSetPanResponder: () => true,
onPanResponderGrant: this.doNothing,
onPanResponderMove: this._handlePanResponderMove,
onPanResponderRelease: this._handlePanResponderEnd,
onPanResponderTerminate: this.doNothing,
});
}
//onPanResponderRelease and onPanResponderTerminate Handler
_handlePanResponderEnd = (event, gestureState) => {
const { navigate } = this.props.navigation;
let tIndex = this.state.index;
if (this.state.dy > 100)
navigate('Second', { index: tIndex, getFunc: this.getName.bind(this) });
else if (this.state.dx > 150) this.setState({ index: tIndex + 1 });
else if (this.state.dx < -150 && tIndex > 0)
this.setState({ index: tIndex - 1 });
};
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
<TouchableHighlight
style={styles.TouchableHighlight}
onPress={() => this.props.navigator.push({id:'SecondScreen'})}>
<View style={styles.author}>
<Text style={styles.author}>Shelter 1</Text>
</View>
</TouchableHighlight>
</View>
);
}
}
class SecondScreen extends React.Component {
static navigationOptions = {
title: 'Second',
};
constructor(props) {
super(props);
this.state = {
index: this.props.navigation.state.params.index,
getFunc: this.props.navigation.state.params.getFunc,
name: 'not set'
};
}
componentWillMount() {
let tName = this.state.getFunc(this.state.index);
this.setState({ name: tName });
}
render() {
const { navigate } = this.props.navigation;
return (
<View style={styles.container}>
</View>
<Button title="Go to Home page" onPress={() => navigate('Home')} />
</View>
);
}
}
const NavigationApp = createStackNavigator({
Home: { screen: HomeScreen },
Second: { screen: SecondScreen },
});
export default createAppContainer(NavigationApp);
});
App.js
import NavApp from "screen";
export default NavApp;
I am getting an error "undefined is not an object (evaluating '_this2.props.navigator.push')"
You could try this
this.props.navigation.push('SecondScreen')
I have a component to add todos AddTodo which works fine and update the state with my added todos and I have a component TodoItems to display the todos in <FlatList/>. I'm using React Native Tab Navigator to switch between components but I'm not sure how to send the state this.state.todos from AddTodo component to TodoItems component.
I have been researching but couldn't find a solution in Tab Navigator but there are plenty of solutions for Stack Navigator.
Component AddTodo
export default class AddTodo extends Component {
constructor(props) {
super(props);
this.state = {
todoText: null,
todos: []
}
}
onAdd = () => {
if (this.state.todoText) {
this.state.todos.push({'todoItem': this.state.todoText});
this.setState({todos: this.state.todos});
}
}
render() {
return(
<View>
<TextInput onChangeText={(text) => {
this.setState({todoText: text});
}} />
<TouchableOpacity onPress={() => {
this.onAdd;
}}>
</View>
);
}
}
Component TodoItems
export default class TodoItems extends Component {
constructor(props) {
super(props);
this.state = {
todosList: []
}
}
render() {
return(
<View>
<FlatList
data={this.state.todosList}
renderItem={(item, index) => {
<Text>{item.todoItem}</Text>
}}
/>
</View>
);
}
}
Component Tabs
import {TabNavigator} from 'react-navigation';
import AddTodo from "./AddTodo";
import TodoItems from "./TodoItems";
var myTabs = TabNavigator(
{
'AddTodo':{screen: AddTodo,},
'TodoItems':{screen: TodoItems, },
},
{
tabBarPosition: 'top',
swipeEnabled: false,
tabBarOptions: {
labelStyle:{
fontSize: 13,
fontWeight: 'bold',
},
indicatorStyle: {
borderBottomColor: '#003E7D',
borderBottomWidth: 2,
},
style:{
backgroundColor: '#F30076',
elevation: 0,
},
},
});
export default myTabs;
Well I think you have two options:
You can use Redux which allows you to globalise your state objects so you can use them all over your app, but it can be rather complicated
https://redux.js.org/
Or you can render TodoItems from within AddTodo:
render() {
return(
<View>
<TextInput onChangeText={(text) => {
this.setState({todoText: text});
}} />
<TouchableOpacity onPress={() => {
this.onAdd;
}}>
</View>
<TodoItems data={this.state.todos} />
);
}
Then you can access that data from within TodoItems:
Hope this helps!
I'm working on a very simple react-native app where I type the name of an artist in a searchbox, retrieve a list of artists from the spotify API and I display this list in FlatList component.
I manage to get the list of artists and I want to save it in the local state so that I pass it to the FlatList component.
The list object looks like this : [{...}, {...}, {...}, {...}]
But it doesn't seem to work and I think that my state is not updating and I don't know what I'm doing wrong.
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
FlatList,
StatusBar,
TextInput,
} from 'react-native';
import colors from './utils/colors';
import { List, ListItem, SearchBar } from 'react-native-elements';
import { searchArtist } from './utils/fetcher';
import { debounce } from 'lodash';
export default class spotilist extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
query: '',
artists: [],
error: null,
refreshing: false,
};
}
render() {
return (
<View style={ styles.container }>
<StatusBar barStyle="light-content" />
<TextInput style={ styles.searchBox }
value={this.state.value}
onChangeText={ this.makeQuery }
/>
<List>
<FlatList
data={this.state.artists}
//renderItem={({item}) => <Text>{item.name}</Text>}
/>
</List>
// {
// this.state.artists.map(artist => {
// return (
// <Text key={artist.id}>{artist.name}</Text>
// )
// })
// }
</View>
);
}
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: this.state.artists,
});
})
.catch((error) => {
throw error;
});
}, 400);
}
Thank you for your help.
UPDATE
I also tried using this without success :
<List>
<FlatList
data={this.state.artists}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={item.name}
avatar={{ uri: item.images[0].url }}
/>
)}
/>
</List>
In the makeQuery function you need to set the response from the server like..
makeQuery = debounce(query => {
searchArtist(query)
.then((artists) => {
console.log(artists); // I have the list
this.setState({
artists: artists, //Here is the change
});
})
.catch((error) => {
throw error;
});
}, 400);
I'm working with ReactNative for the first time. I'm trying to build a ListView based off what a api returns. Here is my component:
'use strict';
var React = require('react-native');
var api = require('../utils/api');
var {
ActivityIndicatorIOS,
ListView,
StyleSheet,
Text,
TextInput,
TouchableHighlight,
View
} = React;
class CheckIn extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoading: false,
dataSource: new ListView.DataSource({
rowHasChanged: (row1, row2) => row1 !== row2
})
};
}
search(searchTerm) {
api.search(searchTerm)
.then((response) => {
this.setState({
isLoading: false,
dataSource: this.state.dataSource.cloneWithRows(response)
})
});
}
renderCustomRow(row) {
return (
<TouchableHighlight style={styles.row}>
<View style={styles.container}>
<Text>{row.name}</Text>
</View>
</TouchableHighlight>
);
}
render() {
return (
<View style={{marginTop: 65}}>
<TextInput
style={styles.search}
placeholder="Search"
onChangeText={(text) => {
this.search(text);
this.setState({isLoading: true});
}}/>
<ListView
dataSource={this.state.dataSource}
renderRow={this.renderCustomRow.bind(this)}
style={styles.listView} />
<ActivityIndicatorIOS
animating={this.state.isLoading}
color='#111'
size="large"></ActivityIndicatorIOS>
</View>
)
}
}
var styles = StyleSheet.create({
search: {
height: 40,
padding: 5,
borderBottomWidth: 2,
borderBottomColor: '#000'
}
});
module.exports = CheckIn;
When I type one letter into the search bar, the activity indicator appears, the api returns a valid JSON response (checked with console.log), and then the activity indicator should disappear, but it doesn't. If I type a second letter, I get the following error:
Cannot read property '_currentElement' of null
Anything I seem to be missing?
I'm using ReactNative 0.14, npm is version 3.5