Related
I'm using WordPress API in my react native app. Everything is working fine except my single post screen. I'm requesting post data, updating state, and rendering data from the state. My app was crashing after going on this screen. I spend hours and I've found out what's the problem however don't how to fix it.
My Screen Code -
import React, { useState, useEffect } from "react";
import {
ActivityIndicator,
Text,
View,
ScrollView,
Image,
useWindowDimensions,
} from "react-native";
import HTMLView from "react-native-htmlview";
import globalStyles from "../constant/globalStyle";
import axios from "axios";
import moment from "moment";
import { Colors } from "../constant/colors";
const PostDetail = ({ route }) => {
const { postId } = route.params;
const [postData, setPostData] = useState({});
const [loaded, setLoaded] = useState(false);
const [source, setSource] = useState("");
useEffect(() => {
const fetchData = async () => {
await axios
.get(
`https://bachrasouthpanchayat.in/wp-json/wp/v2/posts/${postId}?embed=true`
)
.then((response) => {
setPostData(response.data);
setSource(response.data.content.rendered);
setLoaded(true);
})
.catch((error) => console.log(error, error.message));
};
fetchData();
}, []);
const { width } = useWindowDimensions();
return (
<ScrollView
style={{
...globalStyles.container,
backgroundColor: Colors.BACKGROUND_SCREEN,
}}
>
{loaded ? (
<>
<Image
style={{
width: "100%",
height: 250,
}}
source={{ uri: postData.jetpack_featured_media_url }}
resizeMode="cover"
/>
<Text style={globalStyles.primaryHeading}>
{postData.title.rendered}
</Text>
<View style={{ paddingHorizontal: 18 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 10,
}}
>
<Text
style={{
...globalStyles.date,
fontSize: 20,
fontWeight: "700",
}}
>
{moment(postData.date).format("LL")}
</Text>
</View>
<HTMLView
stylesheet={{
p: { fontSize: 20, color: "white", textAlign: "justify" },
h1: {
color: "white",
fontSize: 32,
},
h2: {
color: "white",
fontSize: 28,
},
figure: {
backgroundColor: "red",
width: "100% !important",
height: "100% !important",
},
img: {
width: "100% !important",
backgroundColor: "green",
},
}}
value={source}
imagesMaxWidth={width}
/>
</View>
</>
) : (
<View
style={{
alignItems: "center",
justifyContent: "center",
}}
>
<ActivityIndicator size={60} color={Colors.PRIMARY_GREEN} />
</View>
)}
</ScrollView>
);
};
export default PostDetail;
I've used a bunch of console log statements and found out that "when useEffect runs the request is sent to API but it didn't stay on that line of code to receive a response, instead remaining lines of codes are executed and that's why I was getting the undefined error". However, the state gets updated after some time but I still get the error.
I have used async & await but don't know why all those remaining lines of codes ran before getting a response.
When this page is rendered for the first time, your postData is an empty object:
const [postData, setPostData] = useState({});
and at this point of time you try to access it's children (but it doesn't have them yet):
<Text style={globalStyles.primaryHeading}>
{postData.title.rendered} // here you access the empty object
</Text>
Then the useEffect is called which populates that postData
The solution (just add the question mark after postData.title?.rendered):
<Text style={globalStyles.primaryHeading}>
{postData.title?.rendered}
</Text>
The better solution is to show loading until you have not fetched the component:
if(postData.title?.rendered)
return <ActivityIndicator size={24} color={"#424242"} />
return (
<ScrollView
style={{
...globalStyles.container,
backgroundColor: Colors.BACKGROUND_SCREEN,
}}
>
{loaded ? (
<>
<Image
style={{
width: "100%",
height: 250,
}}
source={{ uri: postData.jetpack_featured_media_url }}
resizeMode="cover"
/>
<Text style={globalStyles.primaryHeading}>
{postData.title.rendered}
</Text>
<View style={{ paddingHorizontal: 18 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
marginBottom: 10,
}}
>
<Text
style={{
...globalStyles.date,
fontSize: 20,
fontWeight: "700",
}}
>
{moment(postData.date).format("LL")}
</Text>
</View>
<HTMLView
stylesheet={{
p: { fontSize: 20, color: "white", textAlign: "justify" },
h1: {
color: "white",
fontSize: 32,
},
h2: {
color: "white",
fontSize: 28,
},
figure: {
backgroundColor: "red",
width: "100% !important",
height: "100% !important",
},
img: {
width: "100% !important",
backgroundColor: "green",
},
}}
value={source}
imagesMaxWidth={width}
/>
</View>
</>
) : (
<View
style={{
alignItems: "center",
justifyContent: "center",
}}
>
<ActivityIndicator size={60} color={Colors.PRIMARY_GREEN} />
</View>
)}
</ScrollView>
ok i got this flatlist contained within a flatlist, and the flatlist from inside, contains dropdowns, inputs, and buttons that i need to capture which data i'm getting from them, and of course save the new data based on what i selected, typed in or clicked, i'm not entirely sure how to do this, this is the code for flatlist
<FlatList
extraData = {this.state}
data = {this.state.dataSource}
renderItem = {({item, index}) => {
return (
<View style={{marginTop: hp('2%')}}>
<TouchableOpacity style={{
alignSelf: 'center',
width: wp('95.7%'),
height: hp('10%'),
backgroundColor: "#ffffff",
}} onPress={() => this.toggleExpanded()}>
<View style={{flex: 1, flexDirection: 'row', justifyContent: 'space-between'}}>
<View style={{flexDirection: 'column', justifyContent: 'center'}}>
<Text numberOfLines={2} ellipsizeMode="tail" style={{
marginLeft: wp('5%'),
width: wp('55%'),//wp('38.2%'), //151
//height: hp('2%'), //19
fontSize: rfv(16),
fontWeight: "500",
fontStyle: "normal",
textAlign: "left",
color: "#707070"
}}>{item.Title}</Text>
<Text style={{
marginLeft: wp('5%'),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#c4c4c4"
}}>{`ID ${item.Id} - ${item.Cliente}`}</Text>
{
!item.IsFavorite ?
<Text style={{
marginLeft: wp('5%'),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "italic",
textAlign: "left",
color: "#c4c4c4"
}}>No favorito</Text>
: null
}
</View>
<View style={{flexDirection: 'column', justifyContent: 'center'}}>
<View style={{flexDirection: 'row', marginRight: wp('3.4%')}}>
<Text style={{
fontSize: rfv(18),
fontWeight: "300",
fontStyle: "normal",
textAlign: "right",
color: "#707070"}}>{`${0}h`}</Text>
<Image style={{marginTop: hp('1%'), marginLeft: wp('3.7%')}} source={this.state.isCollapsed ? Images.expandible : Images.collapsible}/>
</View>
</View>
</View>
</TouchableOpacity>
<Collapsible style={{
alignSelf: 'center',
width: wp('95.7%'),
backgroundColor: "#ffffff",}} collapsed={this.state.isCollapsed}>
<FlatList
//extraData = {this.state}
data = {item.ListReportHistoryResponse}//{DataManager.FavoriteList[moment(this.state.selectedDate).format('YYYY-MM-DD')]}
renderItem = {({ item, index }) => {
return (
<View>
<View style={{flexDirection: 'row'}}>
<Text style={{
marginLeft: wp('5%'),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171"
}}>Etapa</Text>
<Text style={{
marginLeft: wp('42.5%'),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171"
}}>Horas</Text>
</View>
<View style={{flexDirection: 'row'}}>
<ModalDropdown
adjustFrame={style => {
style.top =(Platform.OS === 'ios' ? style.top : style.top - StatusBar.currentHeight);
return style;
}}
dropdownTextStyle={styles.dropdownTextStyle}
dropdownTextHighlightStyle={styles.dropdownTextHighlightStyle}
dropdownStyle={styles.dropdownStageStyle}
defaultValue={item.Etapa}
style={styles.dropStageStyle}
textStyle={{
padding: 0,
margin: 0,
fontSize: rfv(16),
paddingVertical: hp('1.2%'),
fontWeight: 'normal',
fontStyle: 'normal',
textAlign: 'left',
color: item.Etapa /*item.ListReportHistoryResponse[index2].Etapa*/ != 'Selecciona una etapa' ? '#1a1a1a' : '#c4c4c4',
}}
//onSelect={(index, value) => this.setState({SeleccionClientes: value})}
//options={Object.keys(this.state.items)}
onSelect={(index, value) => this.setState({SeleccionClientes: value})}
options={DataManager.ListEtapa}
/>
<View style={styles.InputContainerHours}>
<Text style={styles.InputTextHours}>{item.HorasTrabajadas}</Text>
</View>
<TouchableOpacity style={{marginTop: hp('0.5%'), marginLeft: wp('5.5%')}} onPress={() => this.props.onSubstract}>
<Image source={Images.menos_hora}/>
</TouchableOpacity>
<TouchableOpacity style={{marginTop: hp('0.5%'), marginLeft: wp('2%')}} onPress={() => this.props.onAdd}>
<Image source={Images.mas_hora}/>
</TouchableOpacity>
</View>
<Text style={{
fontSize: rfv(14),
marginLeft: wp('5%'),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171"
}}>Observaciones</Text>
<Input
autoCapitalize="none"
maxLength={100}
inputContainerStyle={styles.InputContainerComentarioOnBlur}
containerStyle={styles.InputComentario}
inputStyle={styles.InputTextHoursRInput}
placeholderTextColor={'#c4c4c4'}
placeholder="(Opcional)"
onChangeText={value => this.setState({})}
/>
<TouchableOpacity style={{alignItems: 'flex-end', alignSelf: 'flex-end'}}>
<Text style={{
marginRight: wp('3.4%'),
marginBottom: hp('3%'),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#1062cc"
}}>Agregar etapa</Text>
</TouchableOpacity>
</View>
)}}/>
</Collapsible>
</View>
)
}}
/>
EDIT: updated with screenshot of the object displayed in flatlist, i still need help getting this to work
<View style={styles.InputContainerHours}>
<Text style={styles.InputTextHours}>{item.HorasTrabajadas}</Text>
</View>
<TouchableOpacity style={{marginTop: hp('0.5%'), marginLeft: wp('5.5%')}} onPress={() => this.props.onSubstract}>
<Image source={Images.menos_hora}/>
</TouchableOpacity>
<TouchableOpacity style={{marginTop: hp('0.5%'), marginLeft: wp('2%')}} onPress={() => this.props.onAdd}>
<Image source={Images.mas_hora}/>
</TouchableOpacity>
the onadd is supposed function that adds to item.HorasTrabajadas, 0.5 in addition to the current value, and the onsubstract is the same but substracts to the current value
and here is the object
edit 2: what if the api contains an object with data saved already, how do i edit that new data?
edit 3: i added a github project with the 3 most important parts of this project, https://github.com/Vasault/HourReg/tree/develop contains 3 files, the most important one HourRegistration.tsx
edit 4: unable to change data that already come from api
You can use the index of your array to identify the selected item, and when you have an index of every time you can update the item on every action according to your need.
Let suppose you have 10 items and on every item, you have to update the state you can get the item and index of that row and can update the state easily.
Here is a simple example that can clear your confusion.
And here is a snack with a working example
https://snack.expo.io/#waheed25/radarada
import React from 'react';
import { SafeAreaView, View, TextInput, FlatList, StyleSheet, Text } from 'react-native';
import Constants from 'expo-constants';
const DATA = [
{
id: 'bd7acbea-c1b1-46c2-aed5-3ad53abb28ba',
title: 'First Item',
},
{
id: '3ac68afc-c605-48d3-a4f8-fbd91aa97f63',
title: 'Second Item',
},
{
id: '58694a0f-3da1-471f-bd96-145571e29d72',
title: 'Third Item',
},
];
export default class App extends React.Component {
state={
observacionesInput: []
}
onTextObservacionesChange = (text, index) => {
var { observacionesInput } = this.state
let myArray = observacionesInput
myArray[index] = text
this.setState({observacionesInput: myArray });
}
toggleExpanded = () => {
this.setState({ isCollapsed: !this.state.isCollapsed });
};
render(){
console.log('state', this.state)
return (
<SafeAreaView>
<Text>Testing</Text>
<FlatList
data={DATA}
renderItem={({ item, index }) => {
return(
<View style={{ marginTop: 20}}>
<TextInput
style={{borderColor: 'gray', borderWidth: 1, height: 50,}}
autoCapitalize="none"
maxLength={100}
placeholderTextColor={'#c4c4c4'}
placeholder="(Opcional)"
onChangeText={value => this.onTextObservacionesChange(value, index)}
/>
</View>
)
}}
/>
</SafeAreaView>
);
}
}
you can make dynamic state by append parentIndex and childIndex to state to uniquely identify of item of child flatlist
you can use onChangeValue for save data into newDataSave object and getDataFromNewSave for get data from newDataSave
Note: you all saving data is saving in newDataSave object. you can console to check it and save into database.
onChangeValue = (key, value) => {
this.setState((prevState) => {
return {
newDataSave: {
...prevState.newDataSave,
[key]: value,
},
};
});
};
getDataFromNewSave = (key) => {
try {
return this.state.newDataSave[key];
} catch (error) {
return "";
}
};
<Input
value={this.getDataFromNewSave(
`input${parentIndex}${childIndex}`
)}
autoCapitalize="none"
maxLength={100}
inputContainerStyle={
styles.InputContainerComentarioOnBlur
}
containerStyle={styles.InputComentario}
inputStyle={styles.InputTextHoursRInput}
placeholderTextColor={"#c4c4c4"}
placeholder="(Opcional)"
onChangeText={(value) => {
this.onChangeValue(
`input${parentIndex}${childIndex}`,
value
);
}}
/>
Full Code:
import React, { Component } from "react";
import {
Text,
StyleSheet,
View,
FlatList,
TouchableOpacity,
Image,
TextInput,
} from "react-native";
import {
widthPercentageToDP as wp,
heightPercentageToDP as hp,
} from "react-native-responsive-screen";
import Collapsible from "react-native-collapsible";
import ModalDropdown from "react-native-modal-dropdown";
const Input = TextInput;
const rfv = (value) => {
return value;
};
const DataManager = {
ListEtapa: ["a", "b", "c"],
};
const Images = {
collapsed: require("./assets/icons/app-icon.png"),
expandible: require("./assets/icons/app-icon.png"),
};
export default class App extends Component {
state = {
isCollapsed: false,
dataSource: [
{
Id: "1",
Title: "abc1",
Cliente: "abc1",
IsFavorite: false,
ListReportHistoryResponse: [
{ Etapa: "abc1", HorasTrabajadas: "abcabc1" },
{ Etapa: "cde1", HorasTrabajadas: "cdecde1" },
],
},
{
Id: "2",
Title: "abc2",
Cliente: "abc2",
IsFavorite: false,
ListReportHistoryResponse: [
{ Etapa: "abc2", HorasTrabajadas: "abcabc2" },
{ Etapa: "cde2", HorasTrabajadas: "cdecde2" },
],
},
],
newDataSave: {},
};
onChangeValue = (key, value) => {
this.setState((prevState) => {
return {
newDataSave: {
...prevState.newDataSave,
[key]: value,
},
};
});
};
getDataFromNewSave = (key) => {
try {
return this.state.newDataSave[key];
} catch (error) {
return "";
}
};
render() {
return (
<View>
<FlatList
extraData={this.state}
data={this.state.dataSource}
renderItem={({ item, index: parentIndex }) => {
return (
<View style={{ marginTop: hp("2%") }}>
<TouchableOpacity
style={{
alignSelf: "center",
width: wp("95.7%"),
height: hp("10%"),
backgroundColor: "#ffffff",
}}
onPress={() => this.toggleExpanded()}
>
<View
style={{
flex: 1,
flexDirection: "row",
justifyContent: "space-between",
}}
>
<View
style={{
flexDirection: "column",
justifyContent: "center",
}}
>
<Text
numberOfLines={2}
ellipsizeMode="tail"
style={{
marginLeft: wp("5%"),
width: wp("55%"), //wp('38.2%'), //151
//height: hp('2%'), //19
fontSize: rfv(16),
fontWeight: "500",
fontStyle: "normal",
textAlign: "left",
color: "#707070",
}}
>
{item.Title}
</Text>
<Text
style={{
marginLeft: wp("5%"),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#c4c4c4",
}}
>{`ID ${item.Id} - ${item.Cliente}`}</Text>
{!item.IsFavorite ? (
<Text
style={{
marginLeft: wp("5%"),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "italic",
textAlign: "left",
color: "#c4c4c4",
}}
>
No favorito
</Text>
) : null}
</View>
<View
style={{
flexDirection: "column",
justifyContent: "center",
}}
>
<View
style={{
flexDirection: "row",
marginRight: wp("3.4%"),
}}
>
<Text
style={{
fontSize: rfv(18),
fontWeight: "300",
fontStyle: "normal",
textAlign: "right",
color: "#707070",
}}
>{`${0}h`}</Text>
<Image
style={{
marginTop: hp("1%"),
marginLeft: wp("3.7%"),
}}
source={
this.state.isCollapsed
? Images.expandible
: Images.collapsible
}
/>
</View>
</View>
</View>
</TouchableOpacity>
<Collapsible
style={{
alignSelf: "center",
width: wp("95.7%"),
backgroundColor: "#ffffff",
}}
collapsed={this.state.isCollapsed}
>
<FlatList
//extraData = {this.state}
data={item.ListReportHistoryResponse} //{DataManager.FavoriteList[moment(this.state.selectedDate).format('YYYY-MM-DD')]}
renderItem={({ item, index: childIndex }) => {
return (
<View>
<View style={{ flexDirection: "row" }}>
<Text
style={{
marginLeft: wp("5%"),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171",
}}
>
Etapa
</Text>
<Text
style={{
marginLeft: wp("42.5%"),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171",
}}
>
Horas
</Text>
</View>
<View style={{ flexDirection: "row" }}>
<ModalDropdown
adjustFrame={(style) => {
style.top =
Platform.OS === "ios"
? style.top
: style.top - StatusBar.currentHeight;
return style;
}}
dropdownTextStyle={styles.dropdownTextStyle}
dropdownTextHighlightStyle={
styles.dropdownTextHighlightStyle
}
dropdownStyle={styles.dropdownStageStyle}
defaultValue={this.getDataFromNewSave(
`SeleccionClientes${parentIndex}${childIndex}`
)}
style={styles.dropStageStyle}
textStyle={{
padding: 0,
margin: 0,
fontSize: rfv(16),
paddingVertical: hp("1.2%"),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color:
item.Etapa /*item.ListReportHistoryResponse[index2].Etapa*/ !=
"Selecciona una etapa"
? "#1a1a1a"
: "#c4c4c4",
}}
//onSelect={(index, value) => this.setState({SeleccionClientes: value})}
//options={Object.keys(this.state.items)}
onSelect={(i, value) =>
// this.setState({ SeleccionClientes: value })
this.onChangeValue(
`SeleccionClientes${parentIndex}${childIndex}`,
value
)
}
options={DataManager.ListEtapa}
/>
<View style={styles.InputContainerHours}>
<Text style={styles.InputTextHours}>
{item.HorasTrabajadas}
</Text>
</View>
<TouchableOpacity
style={{
marginTop: hp("0.5%"),
marginLeft: wp("5.5%"),
}}
onPress={() => this.props.onSubstract}
>
<Image source={Images.menos_hora} />
</TouchableOpacity>
<TouchableOpacity
style={{
marginTop: hp("0.5%"),
marginLeft: wp("2%"),
}}
onPress={() => this.props.onAdd}
>
<Image source={Images.mas_hora} />
</TouchableOpacity>
</View>
<Text
style={{
fontSize: rfv(14),
marginLeft: wp("5%"),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#717171",
}}
>
Observaciones
</Text>
<Input
value={this.getDataFromNewSave(
`input${parentIndex}${childIndex}`
)}
autoCapitalize="none"
maxLength={100}
inputContainerStyle={
styles.InputContainerComentarioOnBlur
}
containerStyle={styles.InputComentario}
inputStyle={styles.InputTextHoursRInput}
placeholderTextColor={"#c4c4c4"}
placeholder="(Opcional)"
onChangeText={(value) => {
this.onChangeValue(
`input${parentIndex}${childIndex}`,
value
);
}}
/>
<TouchableOpacity
style={{
alignItems: "flex-end",
alignSelf: "flex-end",
}}
>
<Text
style={{
marginRight: wp("3.4%"),
marginBottom: hp("3%"),
fontSize: rfv(14),
fontWeight: "normal",
fontStyle: "normal",
textAlign: "left",
color: "#1062cc",
}}
>
Agregar etapa
</Text>
</TouchableOpacity>
</View>
);
}}
/>
</Collapsible>
</View>
);
}}
/>
</View>
);
}
}
const styles = StyleSheet.create({});
SOLVED, in a much better and easier way
on every input, dropdown, etc. set a function and pass 3 parameters
onChangeText={value => this.onChangeTextObservaciones(parentIndex, childIndex, value)
parentIndex (the one from outside flatlist), childIndex (the one from inside), and the value you're editing (blank or not)
const onChangeTextObservaciones = (indexOutside, indexInside, value) => {
this.dataList[indexOutside].ListReportHistoryResponse[indexInside].Title = value;
this.setState({displayDay: this.dataList});
}
change the specific value from the specific element from that object, and change it with the new value, and lastly change the whole new object with the new value.
a friend of mine realize this
I Get the data from firebase real-time and put it I FlatList and when I delete if from Console "Firebase" it's deleted from the List in screen very well but the last item in the Array "Data" couldn't be deleted I don't know why!
I use an onRefresh Props but not help me because we all know the DB is real-time and when we will add any item it's will be in the last without refresh it So it's not work with the last item too and just the loading stuck without re-render the FlatList
Although I use .once('value') when I get data from DB, refresh work but when I refresh after deleting the last item the loading refresh stuck and can't disappear the last item
so how can I solve this issue?
here is my code
import auth from '#react-native-firebase/auth';
import database from '#react-native-firebase/database';
import React, {Component} from 'react';
import {
Dimensions,
FlatList,
Image,
Text,
TouchableOpacity,
View,
} from 'react-native';
import Icon from 'react-native-vector-icons/MaterialIcons';
const {width} = Dimensions.get('window');
export default class PendingOrders extends Component {
constructor(props) {
super(props);
this.state = {
orders: [],
forceUpdate: true,
isFetching: false,
};
}
onRefresh = () => {
this.setState({isFetching: true}, () => this.getApiData());
};
getApiData = () => {
try {
const uid = auth().currentUser.uid;
const Orders = database().ref(`usersOrders/${uid}`);
Orders.on('value', snapshot => {
let orders = [];
snapshot.forEach(childSnapshot => {
if (childSnapshot.val().status == 'pending') {
orders.push({
buildingNumber: childSnapshot.val().buildingNumber,
service: childSnapshot.val().categoryName,
date: childSnapshot.val().date,
time: childSnapshot.val().time,
description: childSnapshot.val().problemDescription,
status: childSnapshot.val().status,
images: childSnapshot.val().Images,
});
this.setState({orders, forceUpdate: false, isFetching: false}, () =>
console.log(this.state.orders),
);
return;
}
});
});
} catch (err) {
console.log('Error fetching data: ', err);
}
};
componentDidMount() {
this.getApiData();
}
_listEmptyComponent = () => {
return (
<View
style={{
flex: 1,
justifyContent: 'center',
alignItems: 'center',
}}>
<Image
style={{
width,
height: width * 0.7,
resizeMode: 'contain',
}}
source={require('../../assets/empty.png')}
/>
<Text
style={{
color: '#000',
marginVertical: 15,
textAlign: 'center',
fontSize: 20,
}}>
No item found
</Text>
</View>
);
};
render() {
console.log('is?', this.state.forceUpdate);
return (
<FlatList
showsVerticalScrollIndicator={false}
data={this.state.orders}
extraData={this.state.isFetching}
onRefresh={() => this.onRefresh()}
ListEmptyComponent={this._listEmptyComponent()}
refreshing={this.state.isFetching}
contentContainerStyle={{
flexBasis: '100%',
}}
renderItem={({item}) => {
return (
<TouchableOpacity
onPress={() =>
this.props.navigation.navigate('OrderDetailsScreen', {
service: item.service,
time: item.time,
date: item.date,
description: item.description,
images: item.images,
status: item.status,
})
}
style={{
margin: 15,
borderRadius: 10,
borderWidth: 1,
flexDirection: 'row',
borderColor: '#ddd',
}}>
<Image
style={{
borderRadius: 10,
borderTopLeftRadius: 0,
borderBottomLeftRadius: 0,
width: 150,
height: 150,
}}
resizeMode="cover"
source={item.images[0]}
/>
<View
style={{
margin: 5,
marginLeft: 10,
justifyContent: 'space-evenly',
}}>
<Text
style={{
marginBottom: 5,
fontWeight: 'bold',
fontSize: 16,
marginTop: 5,
}}>
{item.service}
</Text>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
justifyContent: 'space-around',
}}>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
// paddingHorizontal: 5,
}}>
<Icon name="date-range" color="#AEACAC" size={20} />
<Text style={{paddingHorizontal: 5}}>{item.date}</Text>
</View>
<View
style={{
flexDirection: 'row',
alignItems: 'center',
paddingHorizontal: 5,
}}>
<Icon name="access-time" color="#AEACAC" size={20} />
<Text style={{paddingHorizontal: 5}}>{item.time}</Text>
</View>
</View>
<Text
numberOfLines={1}
ellipsizeMode="tail"
style={{marginBottom: 5, width: 160, marginTop: 5}}>
{item.description}
</Text>
</View>
</TouchableOpacity>
);
}}
keyExtractor={(item, index) => index.toString()}
/>
);
}
}
The key for re activating flatlist is extraData. just change it's value to something else and it will trigger flatlist re rendering. for simplicity pass a boolean value to extraData. every time you want to re active the flatlist, just toggle the value of extraData
Problem
I am creating an app with react native and firebase where users can post to firebase, and then my app will render all of the posts. When I added the code to render the posts from firebase (flatlist), nothing renders. I convert the list from firebase to an array, then use that array in a flatlist to render the posts, but nothing is showing up. I would love some help.
Code
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TextInput, ScrollView, TouchableHighlight, Button, FlatList } from 'react-native';
import { Font } from 'expo';
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "API_KEY",
authDomain: "candidtwo.firebaseapp.com",
databaseURL: "https://candidtwo.firebaseio.com",
storageBucket: "candidtwo.appspot.com",
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
var fontLoaded = false;
var ref = firebase.database().ref('posts');
var newPostRef = ref.push();
var postWidth = 350;
export default class App extends React.Component {
state = {
fontLoaded: false,
};
componentDidMount() {
Expo.Font.loadAsync({
'JosefinSans-Regular.ttf': require('./JosefinSans-Regular.ttf'),
});
}
constructor(props) {
super(props);
this.state = { postInput: ""}
}
getItems() {
var items = [];
var query = ref.orderByKey();
query.once ('value', (snap) => {
snap.forEach ( (child) => {
items.push(childs.val());
});
});
return items;
}
renderItem({ items, index }) {
return <View>
<View style={{width: parseInt(this.state.postWidth), height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
{toString(reverse(items))}
</Text>
</View>
<View style={{width: parseInt(this.state.postWidth), height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
</View>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
</View>;
}
render() {
return (
<View style={styles.container}>
<View style={styles.button}>
<View style={{width: 1, height: 30, backgroundColor: '#e8e8e8'}} />
<Button
onPress={() => this.setState({ fontLoaded: true })}
title="Press Me To Load the App After 15 Seconds!"
color="#fe8200"
accessibilityLabel="Get started anonymously!"
/>
</View>
{this.state.fontLoaded ? (
<View style={styles.container}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Whats on your mind? Create a post!
</Text>
<TextInput
style={{height:40, width: 320, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postInput)=>this.setState({postInput})}
value={this.state.postInput}
/>
<Button
style={{justifyContent: 'center'}}
onPress={() => {
newPostRef.set({ content:this.state.postInput });
this.setState({ postInput: "Your post was succsesfully uploaded! :)" })
}}
title=" + "
color="#fe8200"
/>
<ScrollView>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Adjust the size of posts:
</Text>
<TextInput
style={{height:40, width: 100, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postWidth)=>this.setState({postWidth})}
value={this.state.postWidth}
/>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
<View style={{width: parseInt(this.state.postWidth), height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}} >
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
Why do android phones have higher inital quality than apple phones, but apple phones have a more consistent amount of quality throughout their years?
</Text>
</View>
<View style={{width: parseInt(this.state.postWidth), height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
3
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
1
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
8
</Text>
</View>
<FlatList
data={this.getItems}
renderItem={this.renderItem}
/>
</ScrollView>
</View>) : (null) }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 8,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
button: {
flex: 1,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
});
Picture of firebase database
id have getItems() in your componentWillMount method, and have it set a state ( Such as "firebaseItems" ) or such. Then in your FlatList have your data equal {this.state.firebaseItems}. This will force a re-render once you receive your information. EG
componentWillMount(){
this.getItems();
}
getItems(){
var items = [];
var query = ref.orderByKey();
query.once ('value', (snap) => {
snap.forEach ( (child) => {
items.push(childs.val());
});
}).then(() => {
this.setState({firebaseItems: items});
});
}
then later
<FlatList data = {this.state.firebaseItems}.../>
Problem
I am creating a simple app using React Native and firebase where users can post to the web and see what other people have posted. You can upload things to the servers but when you try and render the posts using a flatlist, the posts say [object Undefined] instead of what is on the server. I would love help fixing this.
Code
import React, { Component } from 'react';
import { StyleSheet, Text, View, Image, TextInput, ScrollView, TouchableHighlight, Button, FlatList } from 'react-native';
import { Font } from 'expo';
import * as firebase from 'firebase';
const firebaseConfig = {
apiKey: "API-key",
authDomain: "candidtwo.firebaseapp.com",
databaseURL: "https://candidtwo.firebaseio.com",
storageBucket: "candidtwo.appspot.com",
};
const firebaseApp = firebase.initializeApp(firebaseConfig);
var fontLoaded = false;
var ref = firebase.database().ref('posts');
var newPostRef = ref.push();
var postWidth = 350;
export default class App extends React.Component {
state = {
fontLoaded: false,
};
componentDidMount() {
Expo.Font.loadAsync({
'JosefinSans-Regular.ttf': require('./JosefinSans-Regular.ttf'),
});
}
constructor(props) {
super(props);
this.state = { postInput: ""}
}
componentWillMount(){
this.getItems();
}
getItems(){
var items = [];
var query = ref.orderByKey();
query.once ('value', (snap) => {
snap.forEach ( (child) => {
items.push(child.val());
});
}).then(() => {
this.setState({firebaseItems: items});
});
}
renderItem({ items, index }) {
return <View>
<View style={{width: 360, height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
{/*{toString(reverse(items))}*/} {toString(items)}
</Text>
</View>
<View style={{width:360, height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
-
</Text>
</View>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
</View>;
}
render() {
return (
<View style={styles.container}>
<View style={styles.button}>
<View style={{width: 1, height: 30, backgroundColor: '#e8e8e8'}} />
<Button
onPress={() => this.setState({ fontLoaded: true })}
title="Get started anonymously!"
color="#fe8200"
accessibilityLabel="Run the app"
/>
</View>
{this.state.fontLoaded ? (
<View style={styles.container}>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Whats on your mind? Create a post!
</Text>
<TextInput
style={{height:40, width: 320, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postInput)=>this.setState({postInput})}
value={this.state.postInput}
/>
<Button
style={{justifyContent: 'center'}}
onPress={() => {
newPostRef.set({ content:this.state.postInput });
this.setState({ postInput: "Your post was succsesfully uploaded! :)" })
}}
title=" + "
color="#fe8200"
/>
<ScrollView>
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 16 }}>
Adjust the size of posts:
</Text>
<TextInput
style={{height:40, width: 100, borderColor: '#303030', borderWidth: 1}}
onChangeText={(postWidth)=>this.setState({postWidth})}
value={this.state.postWidth}
/>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
<View style={{width: parseInt(this.state.postWidth), height: 250, backgroundColor: '#1daff1', alignItems: 'center', justifyContent: 'center', borderRadius: 10, paddingLeft: 10, paddingRight:10}} >
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', textAlign: 'center'}}>
Why do android phones have higher inital quality than apple phones, but apple phones have a more consistent amount of quality throughout their years?
</Text>
</View>
<View style={{width: parseInt(this.state.postWidth), height: 40, borderRadius: 10, backgroundColor: '#147aa8', flexDirection: 'row',paddingLeft:5}} >
<Image source={require('./CandidtwoImages/unlove.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
3
</Text>
<Image source={require('./CandidtwoImages/undislike.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
1
</Text>
<Image source={require('./CandidtwoImages/comments.png')} />
<Text style={{ fontFamily: 'JosefinSans-Regular.ttf', fontSize: 22, color: '#ffffff', paddingRight: 5, paddingTop:5}}>
8
</Text>
</View>
<View style={{width: 1, height: 6, backgroundColor: '#e8e8e8'}} />
<FlatList
data = {this.state.firebaseItems}
renderItem={this.renderItem}
/>
</ScrollView>
</View>) : (null) }
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 8,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
button: {
flex: 1,
backgroundColor: '#e8e8e8',
alignItems: 'center'
},
});
Firebase database layout
posts:
ipaurfiauerspfna(random example key):
content: "hello world"
apiergnfpiarngaenig:
content: "test"
helpful warning message[![warning][2]][2] VirtualizedList: missing keys for items, make sure to specify a random key property on every item or provide a custom keyExtractor.
You are trying to pull firebaseItems from state before you have set it (due to the delay in query response), so this will throw an error. To fix this, declare it is a state variable in your constructor. EG
export default class App extends React.Component {
...
constructor(props) {
super(props);
this.state = {
postInput: "",
firebaseItems: [],
}
}
Looks like you haven't set some variables yet, you are going to want to extend your current code of:
state = {
fontLoaded: false,
};
to
state = {
postInput: "", // Or some other default
fontLoaded: false,
};