How do I prevent data in all other component views on a page from being cleared(rerendering) whenever I start to enter data in other view components? - react-native

On a gallery page, I have a text input field that allows the user to describe the kind of pictures and submit or share with a friend.
When I start to type in the text input field, the images in the gallery are cleared(the page re-renders) and all other input fields or component holding data are cleared.
I need help, how to prevent all data from other components from getting cleared whenever I start to enter data in view component on the page.
I am using functional components in my react native app.
export default function EvidenceSubmission ({route, navigation,navigation:{setParams}}){
//Incidence
const [state, setState]= useState({
selectedIncidence: 'select incidence',
photos:[],
});
/**
*clears images in async storage
*/
const clearStorage= async() =>{
try{
await AsyncStorage.clear();
} catch(exception){
console.log('error clearing items');
}
console.log('items cleared (Evidence Page)');
}
useEffect(()=> {
/*Get images from async storage*/
const getData = async () => {
try {
const value = await AsyncStorage.getItem('photos')
if(value !==null){
setState({
photos: (JSON.parse(value)).reverse(),
})
}
}catch(e){
console.log('error with async getData');
}
}
getData();
}, []);
/navigates to photo preview page/
const navigateToPhotoPreview = (path) =>{
navigation.navigate('PhotoPreviewer',
{
transferredImageItem: path,
})
}
return(
<SafeAreaView style= {globalStyle.MainContainer}>
<StatusBar barStyle="light-content" backgroundColor="#174060"/>
<View flexDirection='column' flex={0.456} marginTop={30}
marginRight ={5}
marginLeft ={5}
borderWidth={0.5}
borderRadius={3}
borderColor='#7E7E7E'>
<View>
<FlatList
data= {state.photos}
keyExtractor={(item, index)=> index}
renderItem={ ({ item}) => (
<TouchableOpacity onPress={() => navigateToPhotoPreview(item) }>
<Image
style={{ width:70, height:75,margin:0.5, resizeMode:'cover'}}
source = {{ uri: "file://"+ item}}
// source = {{ uri: item}}
//source = {{ uri: item.node.image.uri}}
/>
</TouchableOpacity>
)
}
numColumns = {5}
/>
</View>
</View>
<TouchableOpacity style={styles.addPhotoButton}
onPress={()=>{navigation.goBack()}}>
<Add
name={'add'}
size={30}
color="white"
/>
</TouchableOpacity>
<View style={{justifyContent: 'center', margin:5 ,flex: 1.2}}>
<View style={{borderWidth: 1,
borderColor:'#C4C4C4',
borderRadius:5,
width: 270,
marginLeft: 5,
marginBottom: 0,
marginTop: 15}}>
<Picker
selectedValue ={state.selectedIncidence}
style={{height:45, width: 270,
fontFamily:'roboto',
fontStyle:'normal',
fontWeight:'normal'}}
onValueChange={(itemValue, itemIndex) =>
setState({selectedIncidence: itemValue})
}
>
<Picker.Item label="Non-Compliance" value="Non-Compliance"/>
<Picker.Item label="Logistics" value="Logistics"/>
<Picker.Item label="Harassment" value="Harassment"/>
</Picker>
</View>
<View marginBottom={0} marginLeft={5} marginTop={15}>
<Text style={styles.textStyle}>
Description
</Text>
</View>
<View style={{flexDirection:'row'}} >
<View>
<TextInput
style={{height: 70,
width: 270,
borderRadius: 8,
borderColor:'#C4C4C4',
borderWidth: 1, marginLeft: 5}}
onChangeText={(text) => setState({text})}
value={state.text}
multiline={true}
enablesReturnKeyAutomatically={true}>
</TextInput>
</View>
<TouchableOpacity style={styles.microphoneButton}>
<Microphone name="microphone"
size={21}
color='white'
/>
</TouchableOpacity>
</View>
<View marginBottom={0} marginLeft={0} marginTop={15} marginBottom={15} >
<View style={{flexDirection: 'row'}}>
<Text style={{margin:10,marginRight:20,
fontFamily:'roboto', fontSize: 14,
fontWeight:'bold',
}}>
Submit as
</Text>
<View style={styles.radioCircumference}>
<TouchableOpacity style={styles.radioButton}></TouchableOpacity>
</View>
<Text style={{marginLeft: 5, marginRight: 0, marginTop: 10}}>anonymous</Text>
<Text style={{marginLeft: 10, marginRight: 10, marginTop: 10, fontWeight:'bold'}}> or </Text>
<View style={styles.radioCircumference}>
<TouchableOpacity style={styles.radioButton}></TouchableOpacity>
</View>
<Text style={{marginTop: 10,marginLeft: 5, marginRight: 0}}>Sign In</Text>
</View>
</View>
<TouchableOpacity
style={styles.button}
onPress={()=>clearStorage()}
>
<Text style={{color:'white',
alignSelf:'center',
fontSize: 18,
}}>
Next
</Text>
</TouchableOpacity>
</View>
</SafeAreaView>
);
}

Ok, so the first issue here is that you are mutating the state of setState. You are using the same state for your TextInput and Picker components. There are two ways to go about this.
1.Using the same state as u are doing.
const [formState, setFormState] = useState({
selectedIncidence: 'select incidence',
text: "",
photos: [],
});
In this case, when your onChangeText and onValueChange will be
onValueChange = {
(itemValue, itemIndex) =>
setFormState(formState => ({
...formState,
selectedIncidence: itemValue
}))
}
onChangeText = {
(text) => setFormState(formState => ({
...formState,
text
}))
}
2.The second approach is to use different states for each field.
const [selectedIncidence, setSelectedIncidence] = useState('select incidence');
const [text, setText] = useState("");
const [photos, setPhotos] = useState([]);
In this case, when your onChangeText and onValueChange will be
onValueChange = {
(itemValue, itemIndex) =>
setSelectedIncidence(itemValue)
}
onChangeText = {
(text) => setText(text)
}
I hope you find this helpful.

Related

React navigation: navigate from screen to another screen showing previous data

I'm developing a food ordering app using react native and ASP .NET MVC Entity Framework.
I've Search screen with date(field) and vendor list(dropdown) and when I click on search button it will navigate to Orders Screen with date and vendor Id as parameters and with these 2 parameter I fetch data from API and show data with in Orders screen. But problem is first time it's showing correct list and 2nd time with different date and vendor id not updating list in Orders screen while API fetching correct data and showing previous data.
Search Screen Code
<View style={[styles.container, { backgroundColor: colors.background }]}>
<StatusBar style={theme.dark ? "light" : "dark"} />
<CustomLoading isLoading={isLoading} />
{/* Header */}
<View style={styles.header}>
<Animatable.View animation="bounceIn" duration={2000}>
<Image style={styles.logo} source={images.FinalLogoOnLight} />
</Animatable.View>
<Animatable.Text
animation="bounceInLeft"
duration={2000}
style={[styles.screenTitle, { color: colors.black }]}
>
Search/Filter Receipts
</Animatable.Text>
</View>
{/* Form */}
<View>
{/* Date */}
<TouchableOpacity
style={[styles.datePickerWrapper, { backgroundColor: colors.white }]}
onPress={showDatePicker}
>
<DateTimePickerModal
isVisible={isDatePickerVisible}
mode="date"
value={model.date}
onConfirm={handleConfirm}
onCancel={hideDatePicker}
/>
<Text style={{ color: colors.black, fontFamily: "PoppinsRegular" }}>
{moment(model.date).format("YYYY-MM-DD")}
</Text>
<AntDesign name="calendar" color={colors.gray} size={sizes.h2} />
</TouchableOpacity>
{/* DropDown */}
{/* <FormDropDown dropDownRef={dropDownRef} options={vendors} /> */}
<View
style={[
styles.dropDownWrapper,
{
flexDirection: "row",
marginTop: sizes.m10,
justifyContent: "center",
alignItems: "center",
backgroundColor: colors.white,
borderColor: colors.grayLight,
},
]}
>
{model.vendors != null ? (
<FormPicker
placeholderText="Select Vendor"
selectedValue={model.vendorUserId}
onValueChange={(val) => setModel({ ...model, vendorUserId: val })}
data={model.vendors}
/>
) : null}
</View>
{/* Search Button */}
<FormButton
mystyle={{ marginTop: sizes.m20 }}
buttonTitle="Search"
onPress={() => handleSubmit()}
/>
</View>
const handleSubmit = async () => {
try {
if (model.vendorUserId == "" && model.date == "") {
Toast.show("Date or Vendor filed not be empty.");
return;
}
navigation.navigate("RootOrder", {
screen: "SearchedOrders",
params: { searchedOrders: model },
});
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
Searched Order Screen
const [isLoading, setIsLoading] = useState(true);
const [start, setStart] = useState(0);
const [model, setModel] = useState({
data: [],
token: userState.token,
date: searchedOrders.date,
vendorUserId: searchedOrders.vendorUserId,
recordTotal: null,
});
// Functions
const fetchOrderHistoryByDate = async () => {
try {
setIsLoading(true);
var res = await orderHistoryByDate(model);
if (!res.Success) {
console.log("Error: ", res.Data);
setIsLoading(false);
alert(res.Data);
return;
}
var resModel = res.Data;
// console.log(resModel)
setModel({ ...model, data: resModel });
// console.log(resModel);
setIsLoading(false);
} catch (error) {
console.log(error);
setIsLoading(false);
}
};
// Functions END
const init = async () => {
await fetchOrderHistoryByDate();
setIsLoading(false);
};
useEffect(() => {
const unsubscribe = navigation.addListener("focus", () => {
setStart(Math.random() * 10000);
init();
});
return unsubscribe;
}, [start]);
function renderCardItems({ item, index }) {
return (<View style={styles.OrderItemWrapper}>
<LinearGradient
style={[styles.OrderItemWrapperLinearGrad]}
colors={[colors.white, colors.linearGray]}
>
<TouchableOpacity
// style={[styles.OrderItemWrapper, { backgroundColor: colors.white }]}
onPress={() =>
navigation.navigate("OrderDetails", { itemOrderDetails: item })
}
// key={index}
>
<View style={styles.cartItemWrapper}>
<View style={styles.cartItemDetails}>
<Text style={[styles.cartItemText, { color: colors.primary }]}>
{item.ShopName}
{/* ({index}) */}
</Text>
{/* <Text style={{ color: "#000" }}>{item.InvoiceNo}</Text> */}
<Text
style={[styles.cartItemQuantityText, { color: colors.gray }]}
>
{item.Date}
</Text>
<View
style={{
flexDirection: "row",
alignItems: "center",
justifyContent: "space-between",
}}
>
<Text
style={[styles.cartItemPriceText, { color: colors.black }]}
>
AED {item.Total}
</Text>
<View
style={[
styles.statusWrapper,
{ backgroundColor: colors.primary },
]}
>
<Text style={[styles.statusText, { color: colors.white }]}>
{item.Status}
</Text>
</View>
</View>
</View>
<Image
source={{
uri: `${domain}${item.ShopPicture}`,
}}
style={styles.cardItemImage}
/>
</View>
</TouchableOpacity>
</LinearGradient>
</View>);
}
return (
<View style={[styles.container, { backgroundColor: colors.background }]}>
<StatusBar style={theme.dark ? "light" : "dark"} />
<CustomLoading isLoading={isLoading} />
<FlatList
data={model.data}
renderItem={renderCardItems}
keyExtractor={(item, index) => item.InvoiceNo + "_" + index}
showsHorizontalScrollIndicator={false}
showsVerticalScrollIndicator={false}
style={{ marginTop: sizes.m10 }}
style={{
paddingVertical: sizes.m10,
backgroundColor: colors.background,
}}
/>
</View>
you can use navigation.push('screanName') instead of navigation.navigate('screenName')

Text input becomes unusable after submitting data

I am implementing a page in my app that takes a user input and then searches a tv shows API for shows relating to what the user inputs. The form works, and data is returned succesfully, however once the search button has been clicked and the data has been returned, the search bar AND search button becomes unresponsive and will not bring up the keyboard. This was tested on a pixel 3 AVD, however on an iphone X using expo Go, the search button fully disapears from the page!
Here is the code:
import React, {useState} from 'react';
import { StyleSheet, Text, View, TextInput, TouchableOpacity, FlatList, ActivityIndicator, Image } from 'react-native';
import {AntDesign} from '#expo/vector-icons';
const ShowDisplay = ({navigation}) => {
const[text, changeText] = useState('');
const[data, setData] = useState([]);
const check = (item) => {
if(item.show.image){
return(
<Image source={{uri:item.show.image.medium}} style={{width:'100%', height: 200}} />
)
}else{
return(
<Text>Poo</Text>
)
}
}
const showfind = () =>{
fetch('https://api.tvmaze.com/search/shows?q=' + text)
.then((response)=> response.json())
.then((json)=> setData(json))
.catch((error)=>alert(error));
}
const showSearch = (text) =>{
showfind();
}
const changeHandler = (text) =>{
changeText(text)
}
console.log('text is currently '+ text)
return(
<>
<View style={styles.container}>
<View style = {styles.searchform}>
<TextInput
placeholder = 'Search for a show'
style = {styles.search}
onChangeText={text => changeHandler(text)}
/>
<TouchableOpacity onPress={() => showSearch(text)}>
<AntDesign name="search1" size={30} color='black' style = {{marginTop: 19, marginLeft: 15,}}/>
</TouchableOpacity>
</View>
</View>
<View>
{data? (<View style={styles.resultsContainer}>
<FlatList
style = {styles.list}
keyExtractor={(item, index)=> index.toString()}
numColumns= '3'
data={data}
renderItem={({item}) => (
<>
<TouchableOpacity style = {styles.show} onPress={() => navigation.navigate('toShow', {
id: item.show.id,
} )}>
<View style={styles.text}>
{check(item)}
</View>
</TouchableOpacity>
</>
)}
/>
</View>) : (<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#000"/>
</View>)}
</View>
</>
);
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
},
search :{
paddingLeft: 15,
marginTop: 20,
borderRadius:30,
width: 200,
height: 30,
borderWidth: 1,
borderColor: '#000'
}, header : {
marginTop: 20,
fontSize: 30,
},
searchform:{
flexDirection: 'row',
},
show:{
width: '33.3%',
height: 200,
borderStyle: "solid",
borderColor: 'white',
borderWidth: 1,
},
list:{
marginTop: 70,
}
});
export default ShowDisplay;
I thought that maybe because the sumbit button runs a function, maybe the function never stops, and needs to be reset? When the sumbit button is pressed, it makes the call to the API.. I am unsure what is happening.
Looks like you're getting a weird side effect by using a Fragment (<></>) as the root view. The style is getting thrown off in certain circumstances.
Since you don't actually have a reason to use a Fragment as the root (your container view is the root element), removing it seemed to solve the issue. I also removed the unnecessary fragment you were using in the search button.
return(
<View style={styles.container}>
<View style = {styles.searchform}>
<TextInput
placeholder = 'Search for a show'
style = {styles.search}
onChangeText={text => changeHandler(text)}
/>
<TouchableOpacity onPress={() => showSearch(text)}>
<AntDesign name="search1" size={30} color='black' style = {{marginTop: 19, marginLeft: 15,}}/>
</TouchableOpacity>
</View>
<View>
{data? (<View style={styles.resultsContainer}>
<FlatList
style = {styles.list}
keyExtractor={(item, index)=> index.toString()}
numColumns= '3'
data={data}
renderItem={({item}) => (
<TouchableOpacity style = {styles.show} onPress={() =>
navigation.navigate('toShow', {
id: item.show.id,
} )}>
<View style={styles.text}>
{check(item)}
</View>
</TouchableOpacity>
)}
/>
</View>) : (<View style={styles.loadingContainer}>
<ActivityIndicator size="large" color="#000"/>
</View>)}
</View>
</View>
);

React Native Firestore Collection doesnt Render Flatlist

So basically I retrive a collection from Firestore succesfully and succesfully I put it in an array but it doesnt display on the screen, issue is I would say where I wrote paramater post inside my renderPost function. Please help me this should be easy I guess, this is my code:
<!-- language-all: lang-js -->
import Fire, { getPosts } from "../Fire";
import firebase, { database } from "firebase";
require("firebase/firestore");
export default class HomeScreen extends React.PureComponent {
constructor(props) {
super(props);
}
state = {
loading: false,
limit: 2,
post: [],
user: {},
};
onPostsReceived = (post) => {
this.setState({ post: post });
console.log(this.state.post); //it DOES return an array full of objects of collection
};
componentDidMount() {
getPosts(this.onPostsReceived);
//console.log(this.state.post); //returns empty array
}
renderPost = (post) => {
return (
<View style={styles.feedItem}>
<Image
source={
this.state.user.avatar
? { uri: this.state.user.avatar }
: require("../assets/tempAvatar.jpg")
}
style={styles.avatar}
/>
<View style={{ flex: 1 }}>
<View
style={{
flexDirection: "row",
justifyContent: "space-between",
alignItems: "center",
}}
>
<View>
{/* <Text>{console.log(this.state.post)}</Text> */}
<Text style={styles.name}>{}</Text>
<Text style={styles.timestamp}>
{moment(post.timestamp).toDate().toDateString()}
</Text>
</View>
<Ionicons name="ios-more" size={24} color="#73788B" />
</View>
<Text style={styles.post}>{post.text}</Text>
<Image
source={post.image && { uri: post.image }}
style={styles.postImage}
resizeMode="cover"
/>
<View style={{ flexDirection: "row" }}>
<Ionicons
name="ios-heart-empty"
size={24}
color="#73788B"
style={{ marginRight: 16 }}
/>
<Ionicons name="ios-chatboxes" size={24} color="#73788B" />
</View>
</View>
</View>
);
};
render() {
return (
<View style={styles.container}>
<StatusBar
translucent
backgroundColor="white"
barStyle="dark-content"
/>
<ClassicHeader
headerTitle="Eleph"
leftComponent={
<TouchableOpacity
style={{ marginRight: 0, margin: 10 }}
onPress={() =>
this.props.navigation.dispatch(DrawerActions.openDrawer())
}
>
<FontAwesome5 name="bars" size={24} color="#161924" />
</TouchableOpacity>
}
rightComponent={
<TouchableOpacity
style={{ marginLeft: 0, margin: 10 }}
onPress={() => this.props.navigation.navigate("Message")}
>
<Ionicons name="ios-chatboxes" size={24} color="#73788B" />
</TouchableOpacity>
}
/>
<FlatList
style={styles.feed}
data={this.state.post}
//extraData={this.state}
renderItem={({ item, index }) => {
this.renderPost(item);
}}
keyExtractor={(item, index) => String(index)}
//ItemSeparatorComponent={this.renderSeparator}
ListFooterComponent={this.renderFooter}
ListHeaderComponent={this.renderHeader}
onEndReachedThreshold={0}
onEndReached={this.retrieveMore}
showsVerticalScrollIndicator={false}
></FlatList>
</View>
);
}
}
in case my method getPosts from Fire is important here it goes:
export async function getPosts (PostsRetreived) {
var post = [];
var snapshot = await firebase.firestore()
.collection('posts')
.orderBy('timestamp')
.get()
snapshot.forEach((doc) => {
const PostItem = doc.data();
PostItem.id = doc.id;
post.push(PostItem);
});
PostsRetreived(post);
}
Fire.shared = new Fire();
export default Fire;
Omfg...this is why I often hate programming. I lost a week to this and error was this:
renderItem={({ item, index }) => {
this.renderPost(item);
}}
should be renderItem={({ item, index }) =>
this.renderPost(item);
}
yeah i just had to delete one curly bracets, nothing anywhere pointed at this being problematic omg. anyways yeah bye

Open a video player in react native

I'm working on an app where I need to display a player with an embed youtube video. I have retrieve the data of a youtube playlist, and put them in a TouchableHighlight. I want now to display a player in a WebView, but just on top of my list. So when I'm clicking on a video, the list go down and a WebView take the place ? Is it possible ? Here is the code :
Fetching data from YTApi
_fetchPlaylist(){
if(this.state.selectedIndex === 0) {
return(
fetch(`https://www.googleapis.com/youtube/v3/playlistItems?part=snippet,contentDetails&maxResults=${results}&playlistId=PLSlVQ0kIy6qx-f5O3J3GwIEOO5Y52z43-&key=${apiKey}`)
.then(res => res.json())
.then(res => {
const videoId = []
res.items.forEach(item => {
videoId.push(item)
})
this.setState({
data:videoId
})
})
.catch(error => {
console.error(error)
})
)
}
}
displayWebView
displayVideo(videoId){
if(this.state.selectedIndex == 0){
return(
<View style = {{height:300}}>
<WebView
style={{flex:1}}
javaScriptEnabled={true}
source={{uri: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`}}
/>
</View>
)
} else{
return <View></View>
}
}
render
<View style = {styles.containerPlaylist}>
<ScrollView>
<View style = {styles.body}>
{
this.state.data.map((item,i) =>
<TouchableHighlight
key = {item.id.videoId}
onPress = {()=> this.displayVideo(item.id.videoID)}>
<View style = {styles.vids}>
<Image
source = {{uri: item.snippet.thumbnails.medium.url}}
style = {{width: 120, height: 90,marginVertical:30, flexDirection:'column', alignSelf:'flex-start', backgroundColor:'#fff', resizeMode:'contain'}}
/>
<Text style = {styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
Thanks
You need to keep webview in your layout and then need to manage two state,
to show and hide webview/video
to change the url of webview/video
Initialize the state like this along with your other state:
this.state = {
showVideo: false,
videoUrl: "default youtube url or empty string"
}
then create your layout like this and adjust your styling as per your need:
<View style={styles.containerPlaylist}>
<ScrollView>
{this.state.showVideo &&
<View style={{ height: 300 }}>
<WebView
style={{ flex: 1 }}
javaScriptEnabled={true}
source={{ uri: this.state.videoUrl }}
/>
</View>
}
<View style={styles.body}>
{
this.state.data.map((item, i) =>
<TouchableHighlight
key={item.id.videoId}
onPress={() => this.displayVideo(item.id.videoID)}>
<View style={styles.vids}>
<Image
source={{ uri: item.snippet.thumbnails.medium.url }}
style={{ width: 120, height: 90, marginVertical: 30, flexDirection: 'column', alignSelf: 'flex-start', backgroundColor: '#fff', resizeMode: 'contain' }}
/>
<Text style={styles.vidText}>{item.snippet.title}</Text>
</View>
</TouchableHighlight>
)}
</View>
</ScrollView>
</View>
and change your function something like this to display video which has been clicked:
displayVideo(videoId) {
this.setState({
videoUrl: `https://www.youtube.com/embed?v=x57qZ8Ym51s&list=${videoId}`
showVideo: true
});
}

TypeError: Cannot read property of 'data' of undefined

I am using <FlatList/> to render an item for each object of json. This json is stored in this.state.json. Here is my code:
export class MyAppsName extends React.Component {
static navigationOptions = {
title: "App name",
headerLeft: null,
};
constructor() {
super();
this.state = {
index: 0,
isAuthed: false,
data: [
{
name: "Class 1",
grade: 83,
letterGrade: "A+"
},
{
name: "Class 2",
grade: 90,
letterGrade: "B+"
}
],
loading: false
};
}
refresh() {
this.setState({ isAuthed: true });
}
componentWillMount() {
}
render() {
return (
<Root>
<ScrollableTabView initialPage={0} tabBarPosition="top">
<HomeRoute tabLabel="Grades" />
<View
style={{ flex: 1, justifyContent: "center" }}
tabLabel="Settings"
>
</View>
</ScrollableTabView>
</Root>
);
}
}
makeRemoteRequest = () => {
//do request stuff
};
handleRefresh = () => {
this.makeRemoteRequest();
};
renderSeparator = () => {
return (
<View
style={{
height: 1,
width: "86%",
backgroundColor: "black",
}}
/>
);
};
renderHeader = () => {
return <View />;
};
classSelected = className => {
//do stuff
};
renderFooter = () => {
if (!this.state.loading) return null;
return (
<View>
<ActivityIndicator animating size="large" />
</View>
);
};
const HomeRoute = () => (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
style={{ width: width }}
data={this.state.data}
renderItem={({ item }) => (
<View style={{ left: "9%", padding: 6 }}>
<TouchableOpacity onPress={() => this.classSelected(item.name)}>
<Text
style={{
fontSize: 16
}}
>
{item.name}
</Text>
<Text
style={{
fontSize: 12
}}
>
{item.grade}
</Text>
<Text
style={{
fontSize: 12
}}
>
{item.letterGrade}
</Text>
</TouchableOpacity>
</View>
)}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
keyExtractor={item => item.name}
/>
</List>
);
The error I get is 'TypeError: Cannot read property 'data' of undefined' within HomeRoute. I think the error is in this line: data={this.state.data}.
I am defining data correctly? Am I even correctly setting up the FlatList? Or does it have to do with how I am passing down application state?
If you need any other code to be appended to the question, you can just ask in the comments.
Thanks in advance
You've declared the state in is parent component, causing this.state is undefined in its child component.
When you use the HomeRoute component, you need to pass the state data from its parent component.
render() {
return (
<Root>
<ScrollableTabView initialPage={0} tabBarPosition="top">
<HomeRoute tabLabel="Grades" data={this.state.data} />
<View
style={{ flex: 1, justifyContent: "center" }}
tabLabel="Settings"
>
</View>
</ScrollableTabView>
</Root>
);
}
and in the HomeRoute itself, you need to extract data from its props.
const HomeRoute = ({data}) => (
<List containerStyle={{ borderTopWidth: 0, borderBottomWidth: 0 }}>
<FlatList
style={{ width: width }}
data={data}
renderItem={({ item }) => (
<View style={{ left: "9%", padding: 6 }}>
<TouchableOpacity onPress={() => this.classSelected(item.name)}>
<Text
style={{
fontSize: 16
}}
>
{item.name}
</Text>
<Text
style={{
fontSize: 12
}}
>
{item.grade}
</Text>
<Text
style={{
fontSize: 12
}}
>
{item.letterGrade}
</Text>
</TouchableOpacity>
</View>
)}
ItemSeparatorComponent={this.renderSeparator}
ListHeaderComponent={this.renderHeader}
ListFooterComponent={this.renderFooter}
keyExtractor={item => item.name}
/>
</List>
);
Several functions, including HomeRoute, are not defined within the MyAppsName class. Therefore, this in these functions doesn't point to the correct this. I suggest to put them inside the class and bind all the functions that use this.
You get a syntax error because they are not defined properly. Follow the pattern of the definition of render, for example:
makeRemoteRequest() {
Instead of
makeRemoteRequest = () => {