React Native Flatlist overlapping footer? - react-native

I'm getting started with React Native and writing an app. The problem I'm having a problem with the layout/styling. This is a simplified version to show my difficulty.
The Flatlist doesn't know where it's bottom is, it is overlapping the Footer component. I've messed around with it but can't get it to work properly.
import React, { Component } from 'react'
import { FlatList, StyleSheet, Text, View } from 'react-native'
class Header extends Component {
render() {
return (
<View style={styles.headerContainer}>
<Text style={styles.headerText}> </Text>
<Text style={styles.headerText}>
This is the page header!
</Text>
</View>
)
}
}
class Footer extends Component {
render() {
return (
<View style={styles.footerContainer}>
<Text style={styles.footerText}>
This is the page footer!
</Text>
</View>
)
}
}
let myData = [];
for (let i=1; i<=30; i++) {
myData.push({ key: ('My item ' + i) })
}
export default class App extends Component {
render() {
return (
<View style={styles.container}>
<Header />
<View style={styles.listWrapper}>
<FlatList
contentContainerStyle={styles.listContainer}
data={myData}
renderItem={({item}) => <Text style={styles.listItem} >{item.key}</Text>}
contentInset={{top: 0, left: 0, bottom: 50, right: 0}}
/>
</View>
<Footer />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: 'pink'
},
headerContainer: {
backgroundColor: '#333',
height: 60,
},
headerText: {
textAlign: 'center',
fontSize: 20,
color: '#999'
},
listWrapper: {
flex: 1
},
listContainer: {
backgroundColor: 'lightblue',
},
listItem: {
color: 'red',
fontSize: 28
},
footerContainer: {
backgroundColor: '#333',
position: 'absolute',
left: 0,
right: 0,
bottom: 0,
flex: 1,
height: 20
},
footerText: {
textAlign: 'center',
fontSize: 14,
color: '#999'
}
})
The only solution I have is to add the prop:
ListFooterComponent={<View style={{ height: 20 }} />}
to the Flatlist, giving it the same height as the Footer, so it would take up that space. That works, but it seems inelegant. Is there a better way to do it, like with the CSS?
Thanx.

Related

Why is my style not applied to my child component?

Good evening,
I have been trying to create custom card Component in react native that i can use everywhere i need it.
I apply a part of the style inside the component itself, and for the width, layout etc, where i need it. Then i am trying to use the spread operator to "merge" all the properties in the style of the child component. I saw it in a video tutorial, but it's not working for me, and i don't see what is wrong in my code :
import React from 'react';
import { View, Text, StyleSheet, TextInput, Button } from 'react-native';
import Card from '../components/Card';
import Color from '../constants/colors'
import Input from '../components/Input'
const StartGameScreen = props => {
return (
<View style={styles.screen}>
<Text style={styles.title}>Start a New Game!</Text>
<Card style={styles.inputContainer}>
<Text>Select a Number</Text>
<Input />
<View style={styles.buttonContainer}>
<Button title="Reset" color={Color.accent} onPress={() => {}} />
<Button title="Confirm" color={Color.primary} onPress={() => {}} />
</View>
</Card>
</View>
);
};
const styles = StyleSheet.create({
screen: {
flex: 1,
padding: 10,
alignItems: 'center'
},
title: {
fontSize: 20,
marginVertical: 10
},
inputContainer: {
width: 300,
maxWidth: '80%',
alignItems: 'center',
},
buttonContainer: {
flexDirection: 'row',
width: '100%',
justifyContent: 'space-between',
paddingHorizontal: 15
},
input: {
width: 50
}
});
export default StartGameScreen;
And the child component :
import React from 'react';
import { View, StyleSheet } from 'react-native';
const Card = props => {
return (
<View style={{ ...styles.card, ...props.style }}> {props.children}</View>
);
};
const styles = StyleSheet.create({
card: {
shadowColor: 'black',
shadowOffset: { width: 0, height: 2 },
shadowRadius: 6,
shadowOpacity: 0.26,
elevation: 8,
backgroundColor: 'white',
padding: 20,
borderRadius: 10
}
});
export default Card;
Note : I tried with input and card, it's working for none of them.
Is that something that we were able to do but we can't anymore ?
Thank you
Have a nice weekend
Try this:
const Card = props => {
return (
<View style={[styles.card, props.style ]}> {props.children}</View>
);
};

Component cannot read JSON properties

I am trying to figure out why my components are not rendering without errors. I am trying to parse a JSON file that has been saved into a redux store but whenever I call try to reference the redux state through props such as this.props.response.data.specificJSONObject. I will instead get an error that states cannot read property of 'specificJSONObject' of undefined. This problem has plagued all of my components that need the use of the JSON file in the redux store. I initially found that my redux store was not updating(asked question: Redux state not being updated after dispatched actions) when making dispatches but have fixed that and now my redux store is able to store the fetched data it needs but I still get the same error as before.
This is my Summary.js screen for Minimal reproducible code
import React, { Component } from "react";
import {
View,
ImageBackground,
StyleSheet,
Image,
Text,
FlatList
} from "react-native";
import { connect } from "react-redux";
import {
fetchingSuccess,
fetchingRequest,
fetchingFailure,
fetchData
} from "../data/redux/actions/appActions.js";
import PropTypes from "prop-types";
import LinkedName from "./LinkedName";
import ArticleBox from "./ArticleBox";
const styles = StyleSheet.create({
container: {
backgroundColor: "dimgrey",
flex: 1
},
posLeft: {
alignItems: "center",
position: "absolute",
top: 40,
left: 25
},
posRight: {
alignItems: "center",
position: "absolute",
top: 40,
left: 175
},
text: {
textAlign: "center",
color: "#FFF"
},
header_text: {
textAlign: "center",
fontSize: 20,
color: "#FFF"
},
image: {
position: "absolute",
top: 200,
height: 375,
width: 375,
flex: 1
},
topPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 150,
width: 300,
position: "absolute",
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
},
midPanel: {
backgroundColor: "rgba(0.0,0.0,0.0,0.9)",
height: 100,
width: 300,
position: "absolute",
bottom: 120,
alignSelf: "center",
flex: 2,
flexDirection: "column",
borderRadius: 25,
borderWidth: 4
}
});
class Summary extends Component<Props> {
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
componentDidMount = () => {
console.log("STATE", this.state);
};
render() {
return (
<View style={styles.container}>
<ImageBackground
source={require("../assets/images/timescale.png")}
style={styles.image}
resizeMode="contain"
alignSelf="center"
>
<View style={styles.topPanel}>
<Text style={styles.header_text}>Query Taxa</Text>
<View style={styles.posLeft}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon A
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonA}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_a}
latinName={this.state.sciNameA}
/>
</View>
<View style={styles.posRight}>
<Text
style={{ textAlign: "center", color: "#FFF", fontSize: 17 }}
>
Taxon B
</Text>
<Text />
<Text style={styles.text}>{this.state.taxonB}</Text>
<Text />
<LinkedName
url={this.state.hitRecords[0].link_taxon_b}
latinName={this.state.sciNameB}
/>
</View>
</View>
<View style={styles.midPanel}>
<Text style={styles.header_text}>Result</Text>
<Text />
<Text style={styles.text}>TTOL: {this.state.TTOL} MYA</Text>
<Text />
<Text style={styles.text}>median: {this.state.median} MYA</Text>
</View>
<View style={{ position: "absolute", bottom: -35, marginLeft: -5 }}>
<FlatList
horizontal
data={this.state.hitRecords}
renderItem={({ item }) => {
return (
<ArticleBox
title={item.title}
year={item.year}
time={item.time}
author={item.author}
/>
);
}}
itemSeparatorComponent={() => (
<View
style={{
backgroundColor: "#ff8c00",
height: 100,
width: 100
}}
/>
)}
/>
</View>
</ImageBackground>
</View>
);
}
}
Summary.propTypes = {
fetchData: PropTypes.func.isRequired,
response: PropTypes.object.isRequired
};
const mapStateToProps = state => {
return { response: state };
};
const mapStateToDispatch = dispatch => ({
fetchData: url => dispatch(fetchData(url))
});
export default connect(
mapStateToProps,
mapStateToDispatch
)(Summary);
This is the function where I reference the Redux state through props
constructor(props) {
super(props);
this.state = {
taxonA: this.props.response.articles.taxon_a,
taxonB: this.props.response.articles.taxon_b,
sciNameA: this.props.response.articles.scientific_name_a,
sciNameB: this.props.response.articles.scientific_name_b,
hitRecords: Object.keys(this.props.response.articles.hit_records).map(
key => ({ key, ...this.props.response.articles.hit_records[key] })
),
TTOL:
Math.round(this.props.response.articles.sum_simple_mol_time * 10) / 10,
median: Math.round(this.props.response.articles.sum_median_time * 10) / 10
};
// console.log(STORE, this.props.response.articles);
}
In this Instance, I built it right when the state was built but in other components, I will set the state through the componentWillMount function and the problem will still persist.
My expected output should be no errors and my components render properly. The received output is the red screen will the error message cannot read property of 'specificJSONObject' of undefined.
The Answer in the question linked up top helps to solve this problem, but will explained here as well.
In my Summary Screen, the problem is being mapped to response. If you see the MapStatetoProps functions is written like this
const mapStateToProps = state => {
return { response: state };
};
but should be written like this
const mapStateToProps = state => {
return { response: state.fetchingStatus };
};
due to how my Redux store is set up and dispatches(can be seen in the question linked above)

Conditional rendering in react native using map function

I am trying to build a notification page where notifications are being fetched from a remote server using the Notification function, so I initially set the notificationLoaded to false in order to use an ActivityLoader before rendering the notification to the page.
But I am confused about how I can render an ActivityLoader before the notificationLoaded state is set to true.
Thanks in advance
import React, { Component } from 'react';
import {
StyleSheet,
ScrollView,
Dimensions,
Text,
ActivityIndicator,
TouchableOpacity,
TextInput,
View,
StatusBar,
ImageBackground,
KeyboardAvoidingView,
AsyncStorage,
} from 'react-native';
import { Ionicons } from '#expo/vector-icons';
import { Font } from 'expo';
import { createStackNavigator, createAppContainer } from 'react-navigation';
import { KeyboardAwareView } from 'react-native-keyboard-aware-view';
const { height, width } = Dimensions.get('window');
export default class Notification extends Component {
constructor(props) {
super(props);
this.state = {
notification: [],
notificationLoaded: false,
};
}
Notification = () => fetch('http://texotrack.com/api/user/notification.php', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
action: 'fetch',
}),
}).then(response => response.json()).then((responseJsonFromServer) => {
console.log(responseJsonFromServer);
this.setState({
notification: responseJsonFromServer,
notificationLoaded: true,
});
})
componentDidMount() {
this.Notification();
AsyncStorage.getItem('key').then((data) => {
const val = JSON.parse(data);
this.setState({
username: data.name,
photo: data.photo,
email: data.email,
userId: data.id,
address: data.address,
});
});
}
render() {
const notificationList = this.state.notification.map(data => (
<View key={data.msg_id} style={styles.notification}>
<View style={{
display: 'flex', flexDirection: 'row',
}}
>
<Text style={{ color: 'green' }}><Ionicons size={20} color="green" name="ios-notifications" /></Text>
<Text style={{
color: '#c9c9c9', fontSize: 15, fontWeight: 'bold', marginLeft: 5,
}}
>
{data.date_sent}
</Text>
</View>
<View style={styles.notificationBody}>
<Text style={{ color: '#000', fontSize: 16 }}>{data.message}</Text>
</View>
<View style={styles.lineStyle} />
</View>
));
return (
<View style={styles.container}>
<StatusBar
style={{ height: 30 }}
backgroundColor="black"
/>
<View elevation={5} style={styles.headers}>
<Text style={{
fontSize: 25, color: 'green', textTransform: 'uppercase', fontWeight: 'bold',
}}
>
Notification
</Text>
<Text style={styles.topIcon} onPress={this.GoHome}>
<Ionicons name="md-home" size={25} color="black" />
</Text>
</View>
<View style={{ flex: 1, margin: 5 }}>
<ScrollView alwaysBounceVertical contentContainerStyle={{ flexGrow: 1 }} enabled bounces>
{notificationList}
</ScrollView>
</View>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
lineStyle: {
borderWidth: 0.3,
borderColor: '#c9c9c9',
margin: 10,
},
headers: {
height: 50,
marginBottom: 10,
display: 'flex',
flexDirection: 'row',
justifyContent: 'space-between',
textAlignVertical: 'center',
padding: 10,
shadowColor: '#444',
shadowOffset: {
width: 0,
height: 60,
},
shadowRadius: 5,
shadowOpacity: 1.0,
backgroundColor: '#fff',
},
topIcon: {
marginTop: 3,
fontWeight: 'bold',
},
content: {
margin: 10,
flex: 1,
},
notification: {
height: 70,
padding: 10,
},
});
This is what you call conditional rendering. This can be achieved simply by using if conditions in your render function like:
render(){
// if loading true render Loader
if (this.state.notificationStateLoaded === true) {
return(
<ActivityLoader>
)
} else { // when loading false render the other component
return(
<WhateverComponentWhenDataHasArrived/>
)
}
}
React's documentation is pretty cool. Check this official link for conditional rendering
https://reactjs.org/docs/conditional-rendering.html
I dont understand what you need to do in your map function but to do a conditional rendering inside your render you can do :
<View>
{someCondition&& <Text>Hello</Text>}
</View>
else
<View>
{someCondition? <Text>Condition True</Text> : <Text>Condition false</Text>}
</View>

Change color after click on TouchableHighlight in react native

I have done a many research on the color change after press/click. Finally
I got this script for change the state and put it in the TouchableHighlight. But When i clicked on that, only the "underlayColor={'gray'}" is working. Can i get some idea ?
here is my code
import React, { Component } from 'react';
import {
StyleSheet,
Text,
StatusBar ,
TouchableOpacity,
View,
FlatList,
ActivityIndicator,
TouchableHighlight,
PropTypes,
Image,
Alert
} from 'react-native';
import Logo from '../components/Logo';
import Form from '../components/Front';
import {Actions} from 'react-native-router-flux';
export default class Login extends Component<{}> {
constructor() {
super();
this.state = {
dataSource: {},
pressed: false
};
}
izijackpotconfirm() {
Actions.izijackpotconfirm()
}
componentDidMount() {
var that = this;
let items = Array.apply(null, Array(25)).map((v, i) => {
return { id: i+1 };
});
that.setState({
dataSource: items,
});
}
static navigationOptions = {
title: "Izi Jackpot",
headerStyle: {
backgroundColor: "#354247"
},
headerTintColor: "#fff",
headerTitleStyle: {
fontWeight: "bold"
}
};
render() {
var jackpotNumbers = [];
let btn_class = this.state.black ? "NormalSet" : "SelectedSet";
return(
<View style={styles.container}>
<View style={styles.middlecontainer}>
<Text style={styles.logoText}>Please Select 5 Numbers and Submit</Text>
</View>
<FlatList
data={this.state.dataSource}
renderItem={({ item }) => (
<View style={{ flex: 1, flexDirection: 'column', margin: 1 }}>
<TouchableHighlight
onPress={() => { Alert.alert(this.state.pressed) }}
style={[
styles.iziPizi,
this.state.pressed ? { backgroundColor: "blue" } : {}
]}
onHideUnderlay={() => {
this.setState({ pressed: false });
}}
onShowUnderlay={() => {
this.setState({ pressed: true });
}}
underlayColor={'gray'}
>
<Text style={styles.buttonText}>{ item.id}</Text></TouchableHighlight>
</View>
)}
//Setting the number of column
numColumns={5}
keyExtractor={(item, index) => index}
/>
<View style={styles.middlecontainer}>
<TouchableOpacity style={styles.button} onPress={this.izijackpotconfirm} >
<Text style={styles.buttonText}>Submit</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
const styles = StyleSheet.create({
container : {
backgroundColor:'#6F9000',
justifyContent: 'center',
flex: 1,
paddingTop: 30,
},
middlecontainer: {
textAlign: 'center',
alignItems: 'center',
justifyContent :'center',
flex:1
},
signupTextCont : {
flexGrow: 1,
alignItems:'flex-end',
justifyContent :'center',
paddingVertical:16,
flexDirection:'row'
},
signupText: {
color:'rgba(255,255,255,0.6)',
fontSize:16
},
signupButton: {
color:'#ffffff',
fontSize:16,
fontWeight:'500'
},
iziPizi: {
width: 55,
padding: 15,
margin: 5,
borderRadius: 80,
borderWidth: 2,
borderColor: '#FFFFFF',
flex:1
},
iziPiziPress: {
width: 55,
padding: 15,
margin: 5,
backgroundColor:'#1c313a',
borderRadius: 80,
borderWidth: 2,
borderColor: '#FFFFFF',
flex:1
},
button: {
width:300,
backgroundColor:'#1c313a',
borderRadius: 25,
marginVertical: 10,
paddingVertical: 13
},
buttonText: {
fontSize:16,
fontWeight:'500',
color:'#ffffff',
textAlign:'center'
},
logoText : {
color:'#FFFFFF',
fontSize: 16,
fontWeight: '500',
alignItems: 'center',
justifyContent:'center',
},
imageThumbnail: {
justifyContent: 'center',
alignItems: 'center',
height: 100,
},
});
One more thing to say that, i have used FlatList here. Please help on this. Thanks in advance.
The problem is in how you work with the styles inside the Touchable. My advice is to create 2 styles that contain the changes:
style={
this.state.pressed ? styles.pressed : styles.iziPizi
}
Inside the touchable of course:
<TouchableHighlight
onPress={() => { Alert.alert(this.state.pressed) }}
style={
this.state.pressed ? styles.pressed : styles.iziPizi
}
onHideUnderlay={() => {
this.setState({ pressed: false });
}}
onShowUnderlay={() => {
this.setState({ pressed: true });
}}
underlayColor={'gray'}
>
yes. there was a problem in FlatList. but i dont know what is its problem.
use ListItem of native-base instead.
remember that if you want to use ListItem, in constructor change
this.state = {
dataSource: {},
...
}
to
this.state = {
dataSource: [],
}
use array insead. here is a sample code for you.
<View style={{ flex: 1, flexDirection: 'column', margin: 1 }}>
<List>
{this.state.dataSource.map( item =>
<ListItem>
<TouchableHighlight
onPress={() => {}}
onShowUnderlay={this.onPress.bind(this)}
>
<Text style={styles.buttonText}>{ item.id}</Text>
</TouchableHighlight>
</ListItem> )
}
</List>
</View>
Also define onPress method.
another problem in your code is this that you setState of pressed.
it results that all of your element style will be changed. so all of your button backgroundColor will be changed.
so you must define pressed flag for every element in your array and change this flag

Animating Views from top-bottom to side-by-side in React Native

I am trying to create a custom header for my app and I want to animate it in a certain way. I will describe the way it is supposed to be animated.
If you have a look at the image you will see one red View that contains one green View and a blue View. I would like the Views be arranged side-by-side from their current positions while animating.
I have tried to create the code for a collapsing header and the red View that contains everything, is shrinking based on a ScrollView but I cant get the green and blue Views to come side-by-side.
Home.js
const HEADER_EXPANDED_VIEW = 200
const HEADER_COLLAPSED_VIEW = 80
export default class HomeActivity extends Component {
constructor(props) {
super(props)
this.state = {
scrollY: new Animated.Value(0)
}
}
static navigationOptions = {
title: "HomeActivity",
header: null
}
render() {
const headerHeight = this.state.scrollY.interpolate({
inputRange: [0, HEADER_EXPANDED_VIEW - HEADER_COLLAPSED_VIEW],
outputRange: [HEADER_EXPANDED_VIEW, HEADER_COLLAPSED_VIEW],
extrapolate: "clamp"
})
// console.log(headerHeight)
return (
<View style={styles.container}>
<ScrollView
contentContainerStyle={{
padding: 16,
paddingTop: HEADER_EXPANDED_VIEW,
color: "#FFFFFF"
}}
onScroll={Animated.event([
{
nativeEvent: {
contentOffset: {
y: this.state.scrollY
}
}
}
])}
>
<Text style={styles.title}>This is Title</Text>
<Text style={styles.content}>
.....
</Text>
</ScrollView>
<CollapsingHeader headerHeight={headerHeight} />
</View>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1
},
scrollContainer: {
padding: 16
},
title: {
fontSize: 24,
marginVertical: 16
}
})
CollapsingHeader.js
export default class CollapsingHeader extends Component {
render() {
return (
<Animated.View
style={{
height: this.props.headerHeight,
width: Dimensions.get("screen").width,
position: "absolute",
top: 0,
left: 0,
borderWidth: 2,
borderColor: "#FF0000",
backgroundColor: "#212121"
}}
>
<View
style={{
borderWidth: 2,
borderColor: "#00FF00"
}}
>
<MenuButton />
</View>
<View
style={{
flexDirection: "row",
borderWidth: 2,
borderColor: "#0000FF"
}}
>
<View
style={{
flexGrow: 1
}}
>
<Text
style={{
fontFamily: "NunitoSans-Bold",
fontSize: 40,
color: "#FFFFFF"
}}
>
Home
</Text>
</View>
<SortButton />
<SearchButton />
</View>
</Animated.View>
)
}
}
I am relatively new to React Native, please assume I know very little about it.
If you know the headerHeight at which it's going to collapse, then you could add a dynamic flexDirection to the Animated.View style.
style={{
/* ...your Animated.View styles */
flexDirection: this.props.headerHeight < /* collapse height */ ? 'row' : 'column'
}}