whats is the problem in my code?
i can't return list view ? If there is a better strategy, please advise me to make my code better
class Search extends Component {
static navigationOptions =
{
title: 'search',
headerBackTitle: null,
};
state = {
search : '',
output : []
}
handleSearch = (text) => {
this.setState({search: text})
}
searchMe = (search) => {
this.setState({output: <SearchExtend />})
}
render() {
return (
<View style={styles.container}>
<TextInput style={styles.input}
onChangeText={this.handleSearch}
>
</TextInput>
<TouchableOpacity
style={styles.submitButton}
onPress={
()=>this.searchMe(this.state.search)
}
>
<Text>Submit</Text>
</TouchableOpacity>
<View>{this.state.output}</View>
</View>
);
}
}
ActivityIndicator is displayed but the listview does not output
class SearchExtend extends Component{
constructor(props) {
super(props);
this.state = {
isLoading: true
}
}
componentDidMount() {
return fetch('http://example.com/games.php')
.then((response) => response.json())
.then((responseJson) => {
let ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2});
this.setState({
isLoading: false,
dataSource: ds.cloneWithRows(responseJson),
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
ListViewItemSeparator = () => {
return (
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
);
}
onPress(item, rowData){
if(item == 0)
{
this.props.navigation.navigate('Third', {rowData},)
}
else {
this.props.navigation.navigate('Second', {rowData},)
}
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<ListView
dataSource={this.state.dataSource}
renderSeparator= {this.ListViewItemSeparator}
renderRow={(rowData) =>
<TouchableOpacity
onPress={()=> this.onPress(rowData.subCategory, rowData)}>
<Text>{rowData.name}{rowData.subCategory != 0 ? '>' : ''}</Text>
</TouchableOpacity>
}
removeClippedSubviews={false}
/>
</View>
);
}
}
Please help me to fix this problem
The rest of the sections and styles were removed because they were not related to the question
Related
How to limit the quantity of View inside of a scrollview.
My component take too much time to render, because the map function renders too many views. I need to show only 10 views, and when scroll up, renders more 10.
I'm using react native, hooks and typescript.
First of all, if you have a large number of list data don't use scrollview. Because initially, it loads all the data to scrollview component & it costs performance as well.
Use flatlist in react-native instead of scrollview & you can limit the number of items to render in the initially using initialNumToRender. When you reach the end of the scroll position you can call onEndReached method to load more data.
A sample will like this
import React, { Component } from "react";
import { View, Text, FlatList, ActivityIndicator } from "react-native";
import { List, ListItem, SearchBar } from "react-native-elements";
class FlatListDemo extends Component {
constructor(props) {
super(props);
this.state = {
loading: false,
data: [],
page: 1,
seed: 1,
error: null,
refreshing: false
};
}
componentDidMount() {
this.makeRemoteRequest();
}
makeRemoteRequest = () => {
const { page, seed } = this.state;
const url = `https://randomuser.me/api/?seed=${seed}&page=${page}&results=20`;
this.setState({ loading: true });
fetch(url)
.then(res => res.json())
.then(res => {
this.setState({
data: page === 1 ? res.results : [...this.state.data, ...res.results],
error: res.error || null,
loading: false,
refreshing: false
});
})
.catch(error => {
this.setState({ error, loading: false });
});
};
handleRefresh = () => {
this.setState(
{
page: 1,
seed: this.state.seed + 1,
refreshing: true
},
() => {
this.makeRemoteRequest();
}
);
};
handleLoadMore = () => {
this.setState(
{
page: this.state.page + 1
},
() => {
this.makeRemoteRequest();
}
);
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "#CED0CE",
marginLeft: "14%"
}}
/>
);
};
renderHeader = () => {
return <SearchBar placeholder="Type Here..." lightTheme round />;
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View
style={{
paddingVertical: 20,
borderTopWidth: 1,
borderColor: "#CED0CE"
}}
>
<ActivityIndicator animating size="large" />
</View>
);
};
render() {
return (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
data={this.state.data}
renderItem={({ item }) => (
<ListItem
roundAvatar
title={`${item.name.first} ${item.name.last}`}
subtitle={item.email}
avatar={{ uri: item.picture.thumbnail }}
containerStyle={{ borderBottomWidth: 0 }}
/>
)}
keyExtractor={item => item.email}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
onRefresh={this.handleRefresh}
refreshing={this.state.refreshing}
onEndReached={this.handleLoadMore}
onEndReachedThreshold={50}
/>
</List>
);
}
}
export default FlatListDemo;
Check this for more informations.
I changed to Flatlist! But initialNumToRender is not working as expected.
The flatlist is rendering all 150 transactions, not only 15, and i have no idea what to do.
I'm running .map() from another array with all others transactions to create newMonths with only those transactions that i want to use data={newMonths}.
let newMonths = [];
const createArrayMonth = histInfos.map(function (info) {
if (info.created_at.slice(5, 7) === month) {
newMonths = [info].concat(newMonths);
}
});
them, i created my component:
function Item({ value }: { value: any }) {
let createdDay = value.item.created_at.slice(8, 10);
let createdMonth = value.item.created_at.slice(5, 7);
let createdYear = value.item.created_at.slice(2, 4);
function dateSelected() {
if (
month === createdMonth &&
year === createdYear &&
(day === '00' || day == createdDay)
) {
console.log('foi dateSelected');
const [on, setOn] = useState(false);
const Details = (on: any) => {
if (on === true) {
return (
<View style={styles.infos}>
<Text style={styles.TextInfos}>
{' '}
CPF/CNPJ: {value.item.cpf_cnpj}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Criado em: {value.item.created_at}{' '}
</Text>
<Text style={styles.TextInfos}>
{' '}
Endereço da carteira: {value.item.address}{' '}
</Text>
<Text style={styles.TextInfos}> Valor: {value.item.amount}BTC </Text>
</View>
);
} else {
return <View />;
}
};
return (
<View>
<TouchableOpacity
style={styles.card}
onPress={() => setOn(oldState => !oldState)}>
<View style={styles.dateStyleView}>
<Text style={styles.dateStyle}>{createdDay}</Text>
</View>
<View style={styles.left}>
<Text style={styles.title}>Venda rápida</Text>
<Text style={styles.semiTitle}>
{
{
0: 'Pendente',
1: 'Aguardando conclusão',
2: 'Cancelado',
100: 'Completo',
}[value.item.status]
}
</Text>
</View>
<View style={styles.right2}>
<Text style={styles.price}>R$ {value.item.requested_value}</Text>
</View>
</TouchableOpacity>
<View>{Details(on)}</View>
</View>
);
}
}
return dateSelected();}
and i call it here
return (
<ScrollView>
...
<View style={styles.center}>
...
<View style={styles.middle2}>
...
<FlatList
extraData={[refresh, newMonths]}
data={newMonths}
renderItem={(item: any) => <Item value={item} />}
keyExtractor={(item, index) => index.toString()}
initialNumToRender={15}
/>
</View>
</View>
</ScrollView>);}
The scroll bar in the right, start to increase until renders all transactions from the data:
App scroll bar
I'm fetching api(makeup API) in Explore component and using it also in Explorebutton.
Im taking brands as a button in ExploreButtons. When i click button in FlatList element in ExploreButtons I want to see images from api in second FlatList in ExploreButtons. Is there a way componentDidMount can rerender when i click button?
import React, { Component } from 'react'
import { View } from 'react-native'
import ExploreButtons from './ExploreButtons'
export default class Explore extends Component {
constructor(props) {
super(props);
this.state = {
isLoading: true,
makeupApi: 'http://makeup-api.herokuapp.com/api/v1/products.json',
}
}
callbackFunction = (item) => {
this.setState({
makeupApi: 'http://makeup-api.herokuapp.com/api/v1/products.json?brand=' + item,
})
}
async componentDidMount() {
try {
const response = await fetch(this.state.makeupApi);
const responseJson = await response.json();
this.setState({
isLoading: false,
dataSource: responseJson,
}, function () {
});
const reformattedArray = this.state.dataSource.map(obj => {
var rObj = {};
rObj = obj.brand;
return rObj;
});
this.setState({
duplicatesRemoved: reformattedArray.filter((item, index) => reformattedArray.indexOf(item) === index)
})
}
catch (error) {
console.error(error);
}
};
render() {
console.log(this.state.makeupApi)
return (
<View style={{ flex: 1 }}>
<ExploreButtons
api={this.state.dataSource}
removedDuplicatesFromAPI={this.state.duplicatesRemoved}
parentCallback={this.callbackFunction}
makeupApi= {this.state.makeupApi} />
</View>
)
}
}
export default class ExploreButtons extends Component {
getBrandImages = (item) => {
this.props.parentCallback(item)
}
render() {
return (
<View style={{ flex: 1 }}>
<View>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={this.props.removedDuplicatesFromAPI}
renderItem={({ item }) =>
<TouchableOpacity
style={styles.exploreButtons}
onPress={() => {
this.getBrandImages(item)
}}
>
<Text>{item}</Text>
</TouchableOpacity>
}
keyExtractor={item => item}
/>
</View>
<View>
<FlatList
data={this.props.api}
renderItem={({ item }) =>
<View>
<Image source={{ uri: item.image_link }}
style={{
alignSelf: "center",
width: '100%',
height: 300,
}} />
</View>
}
keyExtractor={item => item.id.toString()} />
</View>
</View>
)
}
}
You could just put all the logic inside componentDidMount on another function and call it when you call the callback. As a first very rough approach this would work:
Notes: you don't really need the API URL in the state, put the item on the state and construct the URL based on it.
import React, { Component } from 'react';
import { View } from 'react-native';
import ExploreButtons from './ExploreButtons';
export default class Explore extends Component {
API_URL = 'http://makeup-api.herokuapp.com/api/v1/products.json';
constructor(props) {
super(props);
this.state = {
isLoading: true,
item: null,
dataSource: null,
duplicatesRemoved: [],
};
}
getAPIURL(item) {
if(!item){
return API_URL
}
return `${API_URL}?brand=${item}`;
}
async fetchData(item) {
try {
const url = getAPIURL(item);
const response = await fetch(url);
const responseJson = await response.json();
this.setState({
isLoading: false,
dataSource: responseJson,
item,
});
const reformattedArray = responseJSON.map(({ brand }) => brand);
this.setState({
duplicatesRemoved: reformattedArray.filter(
(item, index) => reformattedArray.indexOf(item) === index,
),
});
} catch (error) {
console.error(error);
}
}
async componentDidMount() {
fetchData();
}
render() {
const { dataSource, duplicatesRemoved, item } = this.state;
return (
<View style={{ flex: 1 }}>
<ExploreButtons
api={dataSource}
removedDuplicatesFromAPI={duplicatesRemoved}
parentCallback={this.fetchData}
makeupApi={getURL(item)}
/>
</View>
);
}
}
export default class ExploreButtons extends Component {
getBrandImages = item => {
this.props.parentCallback(item);
};
render() {
const { removedDuplicatesFromAPI, api } = this.props;
return (
<View style={{ flex: 1 }}>
<View>
<FlatList
horizontal
showsHorizontalScrollIndicator={false}
data={removedDuplicatesFromAPI}
renderItem={({ item }) => (
<TouchableOpacity
style={styles.exploreButtons}
onPress={() => {
this.getBrandImages(item);
}}
>
<Text>{item}</Text>
</TouchableOpacity>
)}
keyExtractor={item => item}
/>
</View>
<View>
<FlatList
data={api}
renderItem={({ item }) => (
<View>
<Image
source={{ uri: item.image_link }}
style={{
alignSelf: 'center',
width: '100%',
height: 300,
}}
/>
</View>
)}
keyExtractor={item => item.id.toString()}
/>
</View>
</View>
);
}
}
How can I make componentDidMount render again?
Not sure what you mean, but I think what you are asking is How can I make componentDidMount *run* again?, and to do that, you would need to have the same code in callbackFunction to run that again. componentDidMount will only run after the first time the component render.
Also notice that if you want to rerender the FlatList you need to pass extraData so it know that it needs to rerender.
So I started learning react-native from videos and they have used ListView but as the ListView will be deprecated soon and will be removed. I get to know that FlatList will be the proper replacement but being a beginner I am not able to migrate to Flatlist.
Error message
ListView has been removed from React Native.See link for more information or use 'deprecated-react-native-listview'
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
FlatList,
ActivityIndicator,
} from 'react-native';
import { createStackNavigator, createAppContainer } from 'react-navigation';
export default class Login extends Component {
static navigationOptions= ({navigation}) =>({
header: null
});
state = {
username : [],
data : []
}
constructor(props) {
super(props);
this.state = {
isLoading: true, // check if json data (online) is fetching
dataSource: [], // store an object of json data
};
}
componentDidMount () {
return fetch("http://172.16.2.109:8090/assessment/getdata2.php?username=test2312")
.then((response) => response.json())
.then((responseJson) => {
// set state value
this.setState({
isLoading: false, // already loading
dataSource: responseJson
});
})
.catch((error) => {
ToastAndroid.show(error.toString(), ToastAndroid.SHORT);
});
}
render() {
const { navigate } = this.props.navigation;
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}
renderItem={({item}) => {
return (
<View>
<Text style={styles.info}>{item.ascendant} is </Text>
</View>
)
}}
keyExtractor={(item, index) => index.toString()}
/>
</View>
);
}
}
const styles = StyleSheet.create({
info: {
fontSize: 20,
}
});
Hope this helps!
import { FlatList } from 'react-native';
<FlatList
data={this.state.dataSource}
showsVerticalScrollIndicator={false}
renderItem={(rowData, index) => (
<Text style={styles.rowViewContainer} onPress={this.GetListViewItem.bind(this, rowData.fruit_name)}>{rowData.fruit_name}</Text>
<View
style={{
height: .5,
width: "100%",
backgroundColor: "#000",
}}
/>
)}
keyExtractor={(item, index) => index.toString()}
style={{marginTop: 10}}
/>
try this code
import { AppRegistry, StyleSheet, FlatList, Text, View, Alert, ActivityIndicator, Platform} from 'react-native';
class Project extends Component {
constructor(props)
{
super(props);
this.state = {
isLoading: true
}
}
componentDidMount() {
return fetch('https://reactnativecode.000webhostapp.com/FruitsList.php')
.then((response) => response.json())
.then((responseJson) => {
this.setState({
isLoading: false,
dataSource: responseJson
}, function() {
// In this block you can do something with new state.
});
})
.catch((error) => {
console.error(error);
});
}
FlatListItemSeparator = () => {
return (
<View
style={{
height: 1,
width: "100%",
backgroundColor: "#607D8B",
}}
/>
);
}
GetFlatListItem (fruit_name) {
Alert.alert(fruit_name);
}
render() {
if (this.state.isLoading) {
return (
<View style={{flex: 1, paddingTop: 20}}>
<ActivityIndicator />
</View>
);
}
return (
<View style={styles.MainContainer}>
<FlatList
data={ this.state.dataSource }
ItemSeparatorComponent = {this.FlatListItemSeparator}
renderItem={({item}) => <Text style={styles.FlatListItemStyle} onPress={this.GetFlatListItem.bind(this, item.fruit_name)} > {item.fruit_name} </Text>}
keyExtractor={(item, index) => index}
/>
</View>
);
}
}
const styles = StyleSheet.create({
MainContainer :{
justifyContent: 'center',
flex:1,
margin: 10,
paddingTop: (Platform.OS === 'ios') ? 20 : 0,
},
FlatListItemStyle: {
padding: 10,
fontSize: 18,
height: 44,
},
});
AppRegistry.registerComponent('Project', () => Project);
I'm creating a small react native app, when i add some code to update some data from firebase it shows me this error on console:
"FirebaseError: Function CollectionReference.doc() requires its first argument to be of type non-empty string, but it was: undefined"
Code of my action:
const updateChat =(newChat)=>{
return (dispatch)=>{
console.log("trying to update: ", newChat);
console.log("trying to update and getting the id: ", newChat.id);
firestore.firestore().collection("chat").doc(newChat.id)
.update(
{
msg: newChat,
}
)
.then(() =>{
dispatch({
type:'UPDATE_CHAT',
})
})
.catch(function(error) {
console.error("Error updating document: ", error);
})
}}
Code of my component:
class SettingsScreen extends React.Component {
static navigationOptions = {
title: 'Chat Screen',
};
state = {
id: "",
chat_input: "",
updated: false,
}
onNewChat = () => {
this.props.addChat(
this.state.chat_input
)
this.setState({
chat_input: ""
});
Keyboard.dismiss();
}
handleUpdate = (id, chat_input) => {
this.setState(
{
id:id,
chat_input: chat_input,
updated: true,
}
)
}
saveUpdate=()=>{
this.props.updateChat(this.state.chat_input)
this.setState({
chat_input: "",
id: "",
})
}
renderItem = ( {item} ) => {
return (
<View style={styles.row}>
<Text style={styles.message} >{item.msg}</Text>
<TouchableOpacity
style={styles.button}
onPress={ () => {this.props.deleteChat(item.id)} }
>
<Image
source={require('../assets/images/trash2.png')}
fadeDuration={0}
style={{width: 30, height: 30}}
/>
</TouchableOpacity>
<TouchableOpacity
style={styles.buttonEdit}
onPress={ () => { this.handleUpdate(item.id, item.msg} }
>
<Image
source={require('../assets/images/edit.png')}
fadeDuration={0}
style={{width: 30, height: 30}}
/>
</TouchableOpacity>
</View>
);
}
render() {
const { thread } = this.props || []
if (!thread) {
return (
<View style={styles.container}>
<Text>Loading...</Text>
</View>
)
}
return (
<View style={styles.container}>
<FlatList
data={thread}
renderItem={this.renderItem}
inverted
keyExtractor={(item, index) => index.toString()}
/>
<KeyboardAvoidingView behavior="padding">
<View style={styles.footer}>
<TextInput
value={this.state.chat_input}
onChangeText={text => this.setState({ chat_input: text })}
style={styles.input}
underlineColorAndroid="transparent"
placeholder="Type something nice"
/>
<TouchableOpacity onPress={
this.state.updated
? this.saveUpdate()
: this.onNewChat.bind(this)
}
>
<Text style={styles.send}>Send</Text>
</TouchableOpacity>
</View>
</KeyboardAvoidingView>
</View>
);
}
}
const mapStateToProps = (state) => {
return {
thread: state.firestore.ordered.chat
}
}
export default compose(
connect(mapStateToProps, {addChat, deleteChat, updateChat}),
firestoreConnect([
{ collection: 'chat'},
]))(SettingsScreen);
You're not passing all the information to your updateChat action. Edit like below
saveUpdate=()=>{
this.props.updateChat({
chatInput: this.state.chat_input,
id: this.state.id
})
this.setState({
chat_input: "",
id: "",
})
}
You'll also need to change your update parameters to this:
firestore.firestore().collection("chat").doc(newChat.id)
.update(
{
msg: newChat.chatInput,
}
)
I have connect socket successfully and getting proper response also from socket connection. State is updating successfully but issue is data is not updating in screen (renderTabSection not render updated data).
class Test extends Component{
constructor (props) {
super(props);
this.animatedValue = new Animated.Value(0);
}
componentDidMount(){
const { restaurantId } = this.props;
this.socket = new WebSocket('wss://test.com/' + id);
this.socket.onopen = (data) =>{
console.log('Socket connected');
}
this.socket.onmessage = (e) => {
this.props.socketData(e.data);
//this.socketUpdate();
};
this.socket.onclose = () => {
this.socket = new WebSocket('wss://test.com/' + id);
}
}
componentWillMount(){
this.props.resetVenueDetails();
this.props.showLoader();
}
componentWillReceiveProps(nextProps){
this.renderTabSection();
}
componentWillUnmount() {
this.socket.close();
}
onTabClick(tabId){
this.props.onTabChange(tabId);
}
renderTabSection(){
let playlist = this.props.playlists;
const {
container,
tabHeaderStyle,
tabHeadStyle,
tabDetailBox,
albumTextStyle,
selectedTabStyle,
hideTabContentStyle
} = styles;
return (
<View style={{flex:1, borderBottomColor: '#000', borderBottomWidth: 2 }}>
<View style={{flex:0,flexDirection:'row'}}>
<View style={tabHeadStyle}>
<TouchableOpacity onPress={this.onTabClick.bind(this,1)}>
<Text style={ [tabHeaderStyle, this.props.tabSelected == 1 ? selectedTabStyle : '' ] }>
Playlist
</Text>
</TouchableOpacity>
</View>
</View>
<View style={[container, this.props.tabSelected == 1 ? "" : hideTabContentStyle]} >
<ScrollView horizontal={true}>
<View style={{ flex: 1,flexDirection: 'row' }}>
{ this.renderPlaylistAlbums('user', playlist) }
</View>
</ScrollView>
</View>
</View>
);
}
renderPlaylistAlbums(type, playlist){
let songsArray = [];
const { tabDetailBox, albumTextStyle } = styles;
playlist = playlist.length > 0 ? playlist : PLAYLIST_SECTION;
playlist.filter( (data, i) => {
if( data.type == type ){
if(data.songs.length > 0 ){
let image = data.songs[0] && data.songs[data.songs.length -1].imageUrl ?
{ uri : data.songs[data.songs.length -1].imageUrl } :
require('../assets/images/playlist.png');
let imageStyle = data.songs[0] && data.songs[data.songs.length -1].imageUrl ?
{ width: '70%',height:'60%', borderRadius: 10 } :
{ width: '60%',height:'60%' };
songsArray.push(
<TouchableOpacity key={i}>
<View style={ tabDetailBox }>
<Image
source={ image }
style={ imageStyle } />
<Text style={ albumTextStyle }>{ data.name }</Text>
</View>
</TouchableOpacity>
);
}
}
});
return songsArray;
}
loadSpinner(){
if( this.props.loading ){
return ( <View style={{ flex:1, position:'absolute', width:'100%' }} ><Spinner /></View> );
}
}
render(){
const { backgroundImage } = styles;
return (
<View style={{ flex: 1 }}>
<ImageBackground source={APP_BACKGROUND_IMAGE.source} style={backgroundImage}>
{ this.loadSpinner() }
<View style={{ flex:1, backgroundColor:'rgba(0,0,0,.7)' }}>
{ this.renderTabSection() }
</View>
</ImageBackground>
<Footer headerText='Home' />
</View>
);
}
}
const mapStateToProps = state => {
return {
error:state.home.error,
loading:state.home.loading,
tabSelected:state.restaurantDetail.tabSelected,
playlists: state.restaurantDetail.playlists
}
}
export default connect(mapStateToProps, { onTabChange, socketData, showLoader, resetVenueDetails })(Test)
I have connect socket successfully and getting proper response also from socket connection. State is updating successfully but issue is data is not updating in screen (renderTabSection not render updated data).
I think you are not setting any state because setState will cause the component to re-render, this is how you can setState
constructor(props){
this.state = {
dataUpdated: false,
anyUseFullVariable: '',
}
}
you can setState when you get response from sockets like this
this.setState({dataUpdated: true})
then you can use the state within render component like this
this.state.dataUpdated
this will re-render the component. This is just an example try doing something similar.
Update
You can also use componentWillRecieveProps, this callback function is triggered whenever there is some change in props
componentWillRecieveProps(nextProps){
if(!isFetching && this.props.data !== nextProps.data) // or implement your preferred condition
this.setState({UpdateList: this.props.data}) // this would call the render function
}
}