Using Expo and the Android App, the <ScrollView> section is not scrolling. I've read through a lot of related questions but can't seem to nail down the issue. Can anyone shed some light for a react native newbie?
On pressing the button the API is requested and then the article headlines are rendered. This all works great but the headlines are not scrollable and don't all fit on the screen. Am I misunderstanding what <ScrollView> does?
import React from 'react'
import { ScrollView, StyleSheet, Text, View, Footer } from 'react-native'
import { Font } from 'expo'
import { Button } from 'react-native-elements'
export default class App extends React.Component {
state = {
fontLoaded: true,
buttonPressed: false,
showButton: true
}
onPress = (e) => {
e.preventDefault()
this.fetchArticlesAsync()
this.setState({
buttonPressed: true,
showButton: false
})
}
async fetchArticlesAsync() {
await fetch('http://content.guardianapis.com/search?show-fields=all&api-key=d8d8c012-6484-4bb4-82d7-2770a7c5d029')
.then(response => response.json())
.then(responseJSON => {
return this.setState({
articleList: responseJSON.response.results
})
})
.catch((error) => {
console.log(error)
})
}
render() {
return (
<ScrollView contentContainerStyle={styles.container}>
{
this.state.fontLoaded &&
<View style={this.state.buttonPressed ? styles.newsContainer : styles.container}>
<Text style={styles.header}>Newsli</Text>
{this.state.showButton &&
<Button buttonStyle={styles.button} onPress={this.onPress} clear text='Feed Me' />}
{this.state.buttonPressed &&
<View>
{this.state.articleList ?
<View style={styles.articleContainer}>
{this.state.articleList.map((article, i) => {
return <View style={styles.articleContainer} key={i}>
<Text style={styles.text}>{article.webTitle}</Text>
</View>
})}
</View> : <Text style={styles.loading}>Loading</Text>}
</View>}
</View>
}
</ScrollView>
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'center',
},
newsContainer: {
flex: 1,
backgroundColor: '#000',
alignItems: 'center',
justifyContent: 'flex-start',
marginTop: 15
},
articleContainer: {
flex: 1,
backgroundColor: '#000',
alignItems: 'flex-start',
justifyContent: 'flex-start',
marginTop: 15
},
header: {
fontSize: 90,
color: '#fff'
},
text: {
fontSize: 20,
color: '#fff',
},
loading: {
fontSize: 24,
color: '#FF69B4'
},
button: {
borderColor: '#fff',
borderWidth: 2
}
})
It was quite small mistake. The ScrollView does not know current device's screen size. You need to have ScrollView inside a View component. Try the following...
<View style={styles.container}>
<ScrollView>
{...}
</ScrollView>
</View>
Next time when you submit a question strip out all nonsense code. Like Font fetching.
Good luck!
Related
I have begun the React Navigation. I have some issue about my project and i wish everybody can support me.
In the app.js, i code:
import React, { Component } from 'react';
import {
StyleSheet,
Text,
View,
TextInput,
TouchableOpacity,
StatusBar,
} from 'react-native';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
class HomeScreen extends Component {
static navigationOptions = {
header: null
}
render() {
return (
<View style={styles.container}>
<StatusBar backgroundColor="#1e90ff"
barStyle="light-content" />
<Text style={styles.welcome}>Login To My App</Text>
<TextInput
style={styles.input}
placeholder="Username" />
<TextInput
style={styles.input}
placeholder="Password"
secureTextEntry
/>
<View style={styles.btnContainer}>
<TouchableOpacity style={styles.userBtn}
onPress{() => this.props.navigation.navigate('Details')}
>
<Text style={styles.btnTxt}>Login</Text>
</TouchableOpacity>
<TouchableOpacity style={styles.userBtn}
onPress{() => this.props.navigation.navigate('Details')}>
<Text style={styles.btnTxt}>Signup</Text>
</TouchableOpacity>
</View>
</View>
)
}
}
class DetailsScreen extends Component {
static navigationOptions = {
title: 'My App',
headerRight: <View/>
}
render() {
return (
<View style={{ flex: 1, alignItems: 'center', justifyContent: 'center' }}>
<Text>Details Screen</Text>
</View>
);
}
}
const RootStack = createStackNavigator({
Home: HomeScreen,
Detail: DetailsScreen
},
{
intialRouteName: 'Home',
defaultNavigationOptions: {
headerStyle: {
backgroundColor: "#1e90ff"
}
},
headerTintColor: "#fff",
headerTitleStyle: {
textAlign: "center",
flex: 1,
}
}
);
const AppContainer = createAppContainer(RootStack);
type Props = {};
export default class App extends Component<Props> {
render() {
return (
<AppContainer />
)
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: "center",
backgroundColor: "lightskyblue",
},
welcome: {
fontSize: 30,
textAlign: "center",
margin: 10,
color: "lightgray",
},
input: {
width: "90%",
backgroundColor: "#fff",
padding: 15,
marginBottom: 10,
marginLeft: 20,
},
btnContainer: {
flexDirection: "row",
justifyContent: "space-between",
width: "90%",
marginLeft: 20,
},
userBtn: {
backgroundColor: "#FFD700",
padding: 15,
width: "45%"
},
btnTxt: {
fontSize: 18,
textAlign: "center",
}
})
Detail: I try to write the code based on the youtube https://www.youtube.com/watch?v=bUesHGYxSLg&list=PLQWFhX-gwJblNXe9Fj0WomT0aWKqoDQ-h&index=4&ab_channel=PradipDebnath and i cannot understand the mistake i met.
When i tried to run by npx react-native run-android in terminal, the warning appeared.
Result: Error failed to instal the app. Make sure you have the Android development environmet set up.
Error: Command fail: gradlew.bat app:installDebug -PreactNativeDevServerPort=8081.
Question: How can i fix the mistake? Because when i star a new file, it did not haave the mistake. Thank for your attention.
Creating Side Menu in React Application.
This would work both with Expo and react
Create a Navigation Service(Navigation-service.ts)
import { NavigationActions } from 'react-navigation';
function navigate(routeName: any, params?: any) {
NavigationActions.navigate({
routeName,
params
});
setRoute(routeName);
}
export default {
navigate
}
After this
create a new file sidemnu.tsx
import NavigationService from Navigation-service.ts;
import { View, TouchableHighlight, Image } from 'react-native';
openHome() {
NavigationService.navigate('Home');
}
render(){
return (
<View style={[this.props.isLoggedIn ? styles.container : styles.containerHidden]}>
<View style={[this.props.isLoggedIn ? styles.tabContainer : styles.tabContainerHidden]}>
<View>
<TouchableHighlight
style={this.props.currentTab === 'Home' ? styles.activeTab : styles.menuBtnContainer}
onPress={() => this.openHome()}
disabled={!this.props.isLoggedIn}
underlayColor='red'
>
<Image
source='any image url'
/>
</TouchableHighlight>
</View>
</View>
</View>
);
}
Add style
import { StyleSheet } from 'react-native';
const styles = StyleSheet.create({
container: {
width: 108,
backgroundColor: '#002A5C',
borderTopColor: '#002457',
borderWidth: 1
},
containerHidden: {
width: 108,
backgroundColor: '#002A5C'
},
tabContainer: {
flex: 1,
flexDirection: 'column'
},
tabContainerHidden: {
flex: 1,
display: 'none'
},
activeTab: {
backgroundColor: '#008cc3',
height: 108,
width: 108,
padding: 10,
alignSelf: 'center',
justifyContent: 'center'
},
menuBtnContainer: {
height: 80,
width: 80,
alignSelf: 'center',
justifyContent: 'center'
},
});
Call in app.tsx file
<Sidemnu />
Whenever you want to display this side menu.
If you have any queries, please write back.
I have to render headerRight conditionally in navigation options.
Right now
static navigationOptions = ({ navigation }) => ({
title: i18N.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight
underlayColor="#E22F39"
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require('../../resources/toolbar/home_white.png')}
/>
</TouchableHighlight>
),
headerTintColor: "white",
headerStyle: {
backgroundColor: "#E22F39"
// top: 30
}
});
My Component
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [
],
stBool: false,
}
}
async componentWillMount() {
BackHandler.addEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress', () => this.props.navigation.goBack());
}
static navigationOptions = ({ navigation }) => ({
title: i18n.t('atmbranchpickHeader'),
headerRight: (
<TouchableHighlight onPress={() => {
navigation.navigate('home');
}}>
<Image style={{ marginRight: 20 }} source={require('../../resources/toolbar/home_white.png')} />
</TouchableHighlight>),
headerTintColor: 'white',
headerStyle: {
backgroundColor: colors.themeColor,
// top: 30
}
});
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={(event) => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View style={styles.listContainer} onPress={(event) => this._selectedItem(item.text)} >
<Text style={styles.listHeader} >{item.header}</Text>
<Text style={styles.listValue} >{item.value}</Text>
</View>
</View>
<Image style={styles.listimgArrow} source={require('../../resources/toolbar/chevron_right_grey.png')} />
</View>
</TouchableWithoutFeedback>
);
}
// Render callBack
render() {
return (
<View style={styles.mainWrapper} >
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}
const styles = StyleSheet.create({
mainWrapper: {
flex: 1,
height: Dimensions.get('window').height,
width: Dimensions.get('window').width,
flexDirection: 'column',
justifyContent: 'flex-start'
},
listRowContainer: {
flexDirection: 'row',
marginTop: 10,
height: 80,
backgroundColor: '#FFFFFF',
justifyContent: 'space-between',
borderBottomWidth: 1,
borderColor: 'lightgray'
},
listinside1Container: {
flexDirection: 'row',
justifyContent: 'flex-start',
alignItems: 'center'
},
listContainer: {
alignItems: 'flex-start',
justifyContent: 'center',
flexDirection: 'column',
backgroundColor: '#FFFFFF',
// borderBottomWidth: 1,
// borderColor: 'lightgray'
},
listHeader: {
color: 'black',
fontFamily: 'Roboto-Medium',
marginLeft: 10,
fontSize: 18,
},
listValue: {
fontFamily: 'Roboto-Regular',
marginTop: 4,
color: 'black',
marginLeft: 10,
fontSize: 14,
},
listImage: {
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listimgArrow: {
// flex: 1,
// flexDirection:'row',
alignSelf: 'center',
height: 25,
width: 25,
margin: 10
},
listVal: {
borderWidth: 1,
borderRadius: 10,
color: 'darkgreen',
borderColor: 'white',
backgroundColor: 'white',
fontWeight: 'bold'
},
});
export default AtmBranchTypeSelect;
From the code I have, headerRight will be displayed in all scenarios. consider I have a scenario like based on state value I have to enable/disable headerRight Button .
for example this.state.stBool? headerRight:(.....) : null
I have to render in this way.Please guide me to achieve this.
You could nest the navigation options inside the render and toggle it based on the state value. Haven't tested and not positively on performace. Hope it helps.
import React, { Component } from "react";
import {
View,
TextInput,
Text,
TouchableOpacity,
TouchableHighlight,
StyleSheet,
AsyncStorage,
BackHandler,
Image,
FlatList,
Dimensions,
TouchableWithoutFeedback
} from "react-native";
import i18n from "../../i18n/i18n.js";
import { colors } from "../../constants/colors.js";
import Storage from "../../utils/AsyncStorage.js";
class AtmBranchTypeSelect extends Component {
// Render callBack
constructor(props) {
super(props);
this.state = {
data: [],
stBool: false
};
}
async componentWillMount() {
BackHandler.addEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
componentWillUnmount() {
BackHandler.removeEventListener("hardwareBackPress", () =>
this.props.navigation.goBack()
);
}
_renderList = ({ item }) => {
return (
<TouchableWithoutFeedback onPress={event => this._selectedItem(item.key)}>
<View style={styles.listRowContainer}>
<View style={styles.listinside1Container}>
<Image style={styles.listImage} source={item.icon} />
<View
style={styles.listContainer}
onPress={event => this._selectedItem(item.text)}
>
<Text style={styles.listHeader}>{item.header}</Text>
<Text style={styles.listValue}>{item.value}</Text>
</View>
</View>
<Image
style={styles.listimgArrow}
source={require("../../resources/toolbar/chevron_right_grey.png")}
/>
</View>
</TouchableWithoutFeedback>
);
};
// Render callBack
render() {
const { stBool } = this.state;
const navigationOptions = ({ navigation }) => ({
title: i18n.t("atmbranchpickHeader"),
headerRight: stBool ? (
<TouchableHighlight
onPress={() => {
navigation.navigate("home");
}}
>
<Image
style={{ marginRight: 20 }}
source={require("../../resources/toolbar/home_white.png")}
/>
</TouchableHighlight>
) : null,
headerTintColor: "white",
headerStyle: {
backgroundColor: colors.themeColor
// top: 30
}
});
return (
<View style={styles.mainWrapper}>
<FlatList data={this.state.data} renderItem={this._renderList} />
</View>
);
}
}
I am looking for a way to achieve the effect shown below in React Native. I have already achieved the navigation bar and tab bar setup using React Navigation.
Now comes the part when I integrate a scroll like the one shown below. Effectively there is a view with lots of rows in it. I tried setting this up in a view wrapped in a ScrollView but this is too simplistic as the view just remains fixed on the screen and I'm looking to move the map with the view.
I'm looking for pseudocode if anything. Can anyone with experience in React Native suggest a good layout to achieve this effect?
I had a bit of fun with this. We could achieve the same effect by just creating a simple overlay on your map to display your list of services. The state of the overlay would be rendered visible from a callback on a button and rendered invisible by the 'refresh' or pull-down from an encapsulating <ScrollView />.
Here's what this component renders:
Here's the component class:
import React, { Component } from 'react';
import {
Text,
View,
TouchableOpacity,
StyleSheet,
ScrollView,
FlatList,
Dimensions,
RefreshControl
} from 'react-native';
export default class SO_MapOverlay extends Component {
constructor(props) {
super(props)
this.state = {
// Simple state variable to hide and show service list
serviceListVisible: false,
refreshing: false,
data: [
{ key: 'item1' },
{ key: 'item2' },
{ key: 'item3' },
{ key: 'item4' },
{ key: 'item5' },
{ key: 'item6' },
{ key: 'item7' },
{ key: 'item8' }
],
}
}
// Simply hides the button and shows the list overlay
showRouteOverview() {
this.setState({ serviceListVisible: true });
}
// Reverses showRouteOverview() when the user pulls down
onRefresh() {
this.setState({ refreshing: true });
this.setState({ serviceListVisible: false });
this.setState({ refreshing: false });
}
// Indicate the offset you want from the BOTTOM of the page
renderSpacer(offset) {
const { height } = Dimensions.get('window');
const spacerOffset = height - parseInt(offset);
return (
<View style={{ height: spacerOffset, backgroundColor: 'transparent' }} />
)
}
// Just renders each item in your flat list
renderItem(itemData) {
return(
<View style={[styles.item]}>
<Text style={{ color: 'black'}}>{itemData.item.key}</Text>
</View>
)
}
renderRefreshControl() {
return (
<RefreshControl
refreshing={this.state.refreshing}
onRefresh={this.onRefresh.bind(this)}
// Note: I'm just hiding the activity monitor with these paramteres
color='transparent'
tintColor='transparent'
/>
)
}
render() {
const { serviceListVisible } = this.state;
const listHeight = 56 * this.state.data.length;
return (
<View style={styles.container}>
<View style={styles.mapContainer}>
<Text style={{color: 'white'}}>I am map.</Text>
<TouchableOpacity
style={[styles.showRouteOverviewButton, serviceListVisible ? styles.invisible : null]}
onPress={() => { this.showRouteOverview() }}>
<Text>Show Services</Text>
</TouchableOpacity>
</View>
<ScrollView
style={[styles.listContainer, serviceListVisible ? styles.visible : null ]}
refreshControl={ this.renderRefreshControl() }>
{ this.renderSpacer(100) }
<FlatList
style={[styles.list, { height: listHeight }]}
data={this.state.data}
renderItem={(itemData) => this.renderItem(itemData)}
keyExtractor={(item, index) => index}
scrollEnabled={false}
/>
</ScrollView>
</View>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
},
visible: {
display: 'flex'
},
invisible: {
display: 'none'
},
mapContainer: {
flex: 1,
alignItems: 'center',
justifyContent: 'center',
backgroundColor: 'gray',
position: 'absolute',
bottom: 0,
right: 0,
left: 0,
top: 0,
},
showRouteOverviewButton: {
position: 'absolute',
bottom: 40,
backgroundColor: 'white',
paddingHorizontal: 20,
paddingVertical: 10,
},
listContainer: {
display: 'none',
flex: 1,
},
list: {
backgroundColor: 'red'
},
item: {
alignItems: 'center',
justifyContent: 'center',
flex: 1,
padding: 20,
backgroundColor: 'white',
}
});
I have a simple tabbar app.
One of the tabs renders a view where I display pictures fetched remotely.
The issue I'm facing is, whenever I switch tabs, the images are re-displayed, despite the fact than neider render is called again, nor shouldComponentUpdate.
This results in a very bad user experience as the view is refreshed over and over again.
Any idea of how I can fix it?
/**
* Sample React Native App
* https://github.com/facebook/react-native
* #flow
*/
import React, { Component } from 'react';
import {
AppRegistry,
StyleSheet,
Text,
View,
TabBarIOS,
ScrollView,
Image,
Dimensions
} from 'react-native';
var {height, width} = Dimensions.get('window');
class TabIssue extends Component {
constructor(props){
super(props);
this.state = {
selectedTab: 'tab1',
items: []
}
}
componentDidMount() {
// Build an array of 60 photos
let items = Array.apply(null, Array(60)).map((v, i) => {
return { id: i, src: 'http://placehold.it/200x200?text='+(i+1) }
});
this.setState({ items });
}
render() {
return (
<TabBarIOS>
<TabBarIOS.Item title="Tab #1"
selected={this.state.selectedTab === 'tab1'}
onPress={() => {
this.setState({ selectedTab: 'tab1', });
}}>
<View style={{marginTop: 20}}>
<Text> toto</Text>
</View>
</TabBarIOS.Item>
<TabBarIOS.Item title="Tab #2"
selected={this.state.selectedTab === 'tab2'}
onPress={() => {
this.setState({ selectedTab: 'tab2', });
}}>
<ScrollView>
<View style={styles.galleryContainer}>
{
this.state.items.map((item) => {
return(
<View key={item.id} style={styles.pictureContainer}>
<Image source={{uri: item.src}} style={styles.picture} />
</View>
)
})
}
</View>
</ScrollView>
</TabBarIOS.Item>
</TabBarIOS>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
justifyContent: 'center',
alignItems: 'center',
backgroundColor: '#F5FCFF',
},
welcome: {
fontSize: 20,
textAlign: 'center',
margin: 10,
},
instructions: {
textAlign: 'center',
color: '#333333',
marginBottom: 5,
},
galleryContainer: {
flexWrap: 'wrap',
flexDirection: 'row',
flex: 1,
backgroundColor: 'red'
},
picture: {
height: width/2 - 4,
width: width/2 -4,
flex: 1,
},
pictureContainer: {
padding: 2
}
});
AppRegistry.registerComponent('TabIssue', () => TabIssue);