I am building an application with React Native and React Navigation, I have made all the settings and it is working, however, when the drawer is fired the image is updated multiple times causing spikes and failures to trigger buttons contained in it.
e.g.:
I am using:
react: 16.8.3,
react-native: 0.59.1,
react-native-ui-kitten: ^3.1.2,
react-navigation: ^3.4.0
I was using version 3 of RN and to try to solve I went back to version 2 but without success.
I put some warnings in the method that executes the image and saw that it is called whenever there is this update.
I already changed the image in different sizes and formats but it also did not help.
I already tested on cell phones and emulators but with no success.
Drawer:
import React, { Component } from 'react';
import {
TouchableHighlight,
View,
ScrollView,
Image,
Platform,
StyleSheet,
} from 'react-native';
import {
RkStyleSheet,
RkText,
RkTheme,
} from 'react-native-ui-kitten';
import Icon from 'react-native-vector-icons/Ionicons';
import Routes from '../../config/navigation/routes';
import logo from '../../assets/smallLogo.png';
export default function SideNavigation(props) {
const onMenuItemPressed = item => {
props.navigation.navigate(item.id);
};
const renderIcon = () => (<Image style={styles.image} source={logo}/>);
const renderMenuItem = item => (
<TouchableHighlight style={styles.container} key={item.id} underlayColor={RkTheme.current.colors.button.underlay} activeOpacity={1} onPress={() => onMenuItemPressed(item)}>
<View style={styles.content}>
<View style={styles.content}>
<RkText style={styles.icon} rkType='moon primary xlarge'><Icon name={item.icon} size={25}/></RkText>
<RkText rkType='regular'>{item.title}</RkText>
</View>
{/*<RkText rkType='awesome secondaryColor small'>{FontAwesome.chevronRight}</RkText>*/}
</View>
</TouchableHighlight>
);
const renderMenu = () => Routes.map(renderMenuItem);
return (
<View style={styles.root}>
<ScrollView showsVerticalScrollIndicator={false}>
<View style={[styles.container, styles.content]}>
{renderIcon()}
</View>
{renderMenu()}
</ScrollView>
</View>
)
}
const styles = RkStyleSheet.create(theme => ({
container: {
height: 60,
paddingHorizontal: 16,
borderTopWidth: StyleSheet.hairlineWidth,
borderColor: theme.colors.border.base,
},
root: {
paddingTop: Platform.OS === 'ios' ? 20 : 0,
backgroundColor: theme.colors.screen.base
},
content: {
flex: 1,
flexDirection: 'row',
alignItems: 'center',
},
icon: {
marginRight: 13,
},
image:{
resizeMode: 'contain',
maxWidth: 125
}
}));
Drawer setup:
import React, {Component} from 'react';
import { View, Text} from 'react-native';
import Login from './screens/login';
import PasswordRecovery from './screens/passwordRecovery';
import Home from './screens/home';
import SideNavigator from './components/sideMenu';
import { bootstrap } from './config/bootstrap';
import {
createDrawerNavigator,
createStackNavigator,
createAppContainer
} from 'react-navigation';
import { withRkTheme } from 'react-native-ui-kitten';
import NavBar from './components/navBar';
import AppRoutes from './config/navigation/routesBuilder';
import Splash from './screens/splash';
bootstrap();
const renderHeader = (navigation, props) => {
const ThemedNavigationBar = withRkTheme(NavBar);
return (
<ThemedNavigationBar navigation={navigation} headerProps={props} />
);
};
const App = createStackNavigator({
Splash: Splash,
Login: Login,
PasswordRecovery: PasswordRecovery,
Main: createDrawerNavigator({
...AppRoutes
},{
contentComponent: props => {
const SideNav = withRkTheme(SideNavigator);
return <SideNav {...props}/>
}
}),
},
{
headerMode: 'none',
})
export default createAppContainer(App);
Routes setup:
import React from 'react';
import _ from 'lodash';
import { createStackNavigator } from 'react-navigation';
import { withRkTheme } from 'react-native-ui-kitten';
import transition from './transitions';
import Routes from './routes';
import NavBar from '../../components/navBar';
const main = {};
const flatRoutes = {};
const routeMapping = (route) => ({
screen: withRkTheme(route.screen),
title: route.title,
});
(Routes).forEach(route => {
flatRoutes[route.id] = routeMapping(route);
main[route.id] = routeMapping(route);
route.children.forEach(nestedRoute => {
flatRoutes[nestedRoute.id] = routeMapping(nestedRoute);
});
});
const renderHeader = (navigation, props) => {
const ThemedNavigationBar = withRkTheme(NavBar);
return (
<ThemedNavigationBar navigation={navigation} headerProps={props} />
);
};
const DrawerRoutes = Object.keys(main).reduce((routes, name) => {
const rawRoutes = routes;
rawRoutes[name] = {
name,
screen: createStackNavigator(flatRoutes, {
initialRouteName: name,
headerMode: 'screen',
cardStyle: { backgroundColor: 'transparent' },
transitionConfig: transition,
defaultNavigationOptions: ({ navigation }) => ({
gesturesEnabled: false,
header: (props) => renderHeader(navigation, props),
}),
}),
};
return rawRoutes;
}, {});
const AppRoutes = DrawerRoutes;
Related
I am using expo and want to show the splash screen until images are all loaded. I am loading images from a url.
What is supposed to happen is when onLoad is called, numberOfImagesLoaded increases by 1, and useAppIsReady is run again. Then there is a check
const imagesAreLoaded = numberOfImagesLoaded > 2;.
When numberOfImagesLoaded > 2, appIsReady is true, then onLayoutRootView gets called and the splash screen gets hidden.
However, I am not sure why when onLoad is called, numberOfImagesLoaded in useAppIsReady does not seem to increase.
App.js
import { StatusBar } from "expo-status-bar";
import { Image, StyleSheet, Text, View } from "react-native";
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import React, { useCallback, useState } from "react";
import useAppIsReady from "./useAppIsReady";
import data from "./data";
SplashScreen.preventAutoHideAsync();
export default function App() {
const [numberOfImagesLoaded, setNumberOfImagesLoaded] = useState(0);
const appIsReady = useAppIsReady(numberOfImagesLoaded);
const onLayoutRootView = useCallback(async () => {
if (appIsReady) {
await SplashScreen.hideAsync();
}
}, [appIsReady]);
if (!appIsReady) {
return null;
}
return (
<View onLayout={onLayoutRootView} style={styles.container}>
<Text>Open up App.js to start working on your app!</Text>
<Images setNumberOfImagesLoaded={setNumberOfImagesLoaded} />
<StatusBar style="auto" />
</View>
);
}
const Images = ({ setNumberOfImagesLoaded }) => {
return (
<>
{data.map(({ imgSrc }) => {
return (
<MyImage
key={imgSrc}
imgSrc={imgSrc}
setNumberOfImagesLoaded={setNumberOfImagesLoaded}
/>
);
})}
</>
);
};
const MyImage = ({ imgSrc, setNumberOfImagesLoaded }) => {
const onLoad = () => {
setNumberOfImagesLoaded((prev) => prev + 1);
};
return (
<Image source={{ uri: imgSrc }} style={styles.image} onLoad={onLoad} />
);
};
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: "#fff",
alignItems: "center",
justifyContent: "center",
},
image: {
flex: 1,
width: "100%",
},
});
useAppIsReady.js
import { useFonts } from "expo-font";
import * as SplashScreen from "expo-splash-screen";
import React, { useState, useCallback } from "react";
const useAppIsReady = (numberOfImagesLoaded) => {
const [appIsReady, setAppIsReady] = useState(false);
// Load fonts
const baseAssetsPath = "./assets/fonts";
const fonts_by_path = {
"DMSans-Regular": require(`${baseAssetsPath}/DMSans-Regular.ttf`),
"DMSans-Bold": require(`${baseAssetsPath}/DMSans-Bold.ttf`),
"DMSans-BoldItalic": require(`${baseAssetsPath}/DMSans-BoldItalic.ttf`),
"DMSans-Medium": require(`${baseAssetsPath}/DMSans-Medium.ttf`),
"DMSans-MediumItalic": require(`${baseAssetsPath}/DMSans-MediumItalic.ttf`),
"DMSans-Italic": require(`${baseAssetsPath}/DMSans-Italic.ttf`),
};
const [fontsLoaded] = useFonts(fonts_by_path);
// Load images
console.log(numberOfImagesLoaded);
const imagesAreLoaded = numberOfImagesLoaded > 2;
// const imagesAreLoaded = true;
if (fontsLoaded && imagesAreLoaded && !appIsReady) {
setAppIsReady(true);
}
return appIsReady;
};
export default useAppIsReady;
Here is a link to the repo
https://github.com/Bijig0/loading-imgs-testing
i implemented a little app for learning, and imported data from a local database. I realized it with this code:
import { StatusBar } from 'expo-status-bar';
import { Platform, FlatList, StyleSheet, Text, View, TextInput, Button, SafeAreaView, ScrollView } from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { Icon } from 'react-native-elements'
import * as React from 'react';
import { ListItem, Avatar} from 'react-native-elements';
import { Header } from 'react-native-elements';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { List, Colors } from 'react-native-paper';
import Moment from 'moment';
const MyComponent = () => {
const [expanded, setExpanded] = React.useState(true);
}
const handlePress = () => setExpanded(!expanded);
export default class Db extends React.Component {
state = {
data: [],
}
componentWillMount() { //load data from a remote endpoint (where to instantiate the network request)
this.fetchData();
}
fetchData = async () => {
const response = await fetch('http://localhost:3000/Ph');
const json = await response.json();
this.setState({data: json}); //js object notation
}
keyExtractor = (x, i) => i //extract item
renderItem = ({ item }) => (
<List.Section>
<List.Accordion
theme = {{colors: { primary: '#00A1DE'}}}
style= {{ backgruondColor: '#00A1DE' }}
titleStyle = {styles.titleContainer}
title = {item.Name}
left= { props => <List.Icon {...props} icon = "account" /> }>
<List.Item
titleStyle={styles.textContainer}
title= {item.Geburtsdatum} left= { props => <List.Icon {...props} icon = "cake-variant" /> }></List.Item>
<List.Item
titleStyle={styles.textContainer}
title={item.hobbies} left= { props => <List.Icon {...props} icon = "table-tennis" /> }></List.Item>
</List.Accordion>
</List.Section>
)
render() {
return (
<FlatList
keyExtractor={this.keyExtractor}
data = {this.state.data}
renderItem = { this.renderItem }
/>
)
}
}
const styles = StyleSheet.create({
textContainer: {
flex: 1,
height: '90%',
width: '90%',
},
titleContainer: {
fontWeight: 'bold',
}
});
This works well with the Expo web view, but when I scan the QR Code from my Iphone, the data are not shown. Only footer and header with bottom menue...
I get also the yellow warning because of my ComponentWillMount - function. But do not know, if this is the real reason for my problem. What do you think, why it does not work on my Iphone? I am using the newest expo Version, the newest React Native version and so on... The Code above works well with the Web View from Expo...
Add you IP address instead of localhost
1.) Run cmd
2.) type ipconfig
3.) Scroll Down to IPv4 Address. . . . . . . . . . . : 192.168.**.**
4.) Copy this IP and replace it in place of localhost
5.) Done
Get your IP like this
So Suppose if your IPv4 address is - 192.168.100.84 then
Your fetch should look like this
await fetch('http://192.168.100.84:3000/Ph');
Your component should look like this..Notice I've used Function component here
import * as React from 'react';
import Constants from 'expo-constants';
import { StatusBar } from 'expo-status-bar';
import {
Platform,
FlatList,
StyleSheet,
Text,
View,
TextInput,
Button,
SafeAreaView,
ScrollView,
} from 'react-native';
import Ionicons from 'react-native-vector-icons/Ionicons';
import { Icon } from 'react-native-elements';
import { ListItem, Avatar } from 'react-native-elements';
import { Header } from 'react-native-elements';
import { SafeAreaProvider } from 'react-native-safe-area-context';
import { List, Colors } from 'react-native-paper';
import Moment from 'moment';
const BaseURL = '192.168.100.84'; // Here your IPv4 address
export default function App() {
const [Data, setData] = React.useState([]);
React.useEffect(() => {
fetchData();
}, []);
const fetchData = async () => {
const response = await fetch(`http://${BaseURL}:3000/Ph`);
const json = await response.json();
setData(json);
};
const keyExtractor = (x, i) => i; //extract item
const renderItem = ({ item }) => (
<List.Section>
<List.Accordion
theme={{ colors: { primary: '#00A1DE' } }}
style={{ backgruondColor: '#00A1DE' }}
titleStyle={styles.titleContainer}
title={item.Name}
left={(props) => <List.Icon {...props} icon="account" />}>
<List.Item
titleStyle={styles.textContainer}
title={item.Geburtsdatum}
left={(props) => (
<List.Icon {...props} icon="cake-variant" />
)}></List.Item>
<List.Item
titleStyle={styles.textContainer}
title={item.hobbies}
left={(props) => (
<List.Icon {...props} icon="table-tennis" />
)}></List.Item>
</List.Accordion>
</List.Section>
);
return (
<FlatList
keyExtractor={keyExtractor}
data={Data}
renderItem={renderItem}
/>
);
}
const styles = StyleSheet.create({
textContainer: {
flex: 1,
height: '90%',
width: '90%',
},
titleContainer: {
fontWeight: 'bold',
},
});
I just created a React Native app with Expo using expo init. Everything went fine. Then I went on and created a Home screen like so:
import React from 'react';
import { View, TextInput } from 'react-native';
export default class HomeScreen extends React.Component {
state = {
searchText: ""
}
render() {
return (
<View
style={{
flex: 1,
backgroundColor: 'white'
}}
>
<TextInput
placeholder="Search..."
value={this.state.searchText}
onChangeText={ (searchText) => this.setState({ searchText }) }
/>
</View>
);
}
}
I added it to my AppNavigator...
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import HomeScreen from '../screens/HomeScreen';
const AppNavigator = createStackNavigator({
Home: HomeScreen
});
export default createAppContainer(AppNavigator);
...which I in turn added to App.js:
import React from 'react';
import { StyleSheet, View, StatusBar, Platform } from 'react-native';
import { AppLoading } from 'expo';
import AppNavigator from './navigation/AppNavigator';
export default class App extends React.Component {
state = {
isLoadingComplete: false
}
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
}
return (
<View style={styles.container}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
_loadResourcesAsync = async () => { };
_handleLoadingError = error => {
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
alignItems: 'center',
justifyContent: 'center',
},
});
Now, my problem is that my app looks like this:
As you can see, the horizontal margins are way too big, but I never set them to be like that. What am I doing wrong? I want the TextInput to stretch across the full screen.
Your HomeScreen Component will be as wide as its contents. You did not specify the width of the main View, therefore, it adjusted itself to be as wide the components inside. What you can do to resolve this issue is that you must provide width property in your Top level View's style.
And in order to get the Width of the screen, you can import Dimensions from react-native and use that like below:
const windowWidth = Dimensions.get('window').width;
and finally, you can assign the windowWidth to your view like so:
style={{flex:1, width: windowWidth, backgroundColor: 'white'}}
I'm adding in the react-navigation header a drawerMenuButton to open the Drawer Menu.
The icon appears normally but when pressed returns the error quoted.
import React from 'react';
import { View, Dimensions } from 'react-native';
import { Button, Icon } from 'native-base';
import { createAppContainer, createStackNavigator, createDrawerNavigator } from 'react-navigation';
...
const DrawerConfig = {
drawerWidth: Dimensions.get('window').width * 0.75,
contentComponent: ({ navigation }) => {
return(<MenuDrawer navigation={navigation} />)
}
}
const HomeNavigator = createStackNavigator ({
...
}, {
defaultNavigationOptions: ({ navigation }) => {
return {
headerTitleStyle: {
fontWeight: 'bold'
},
headerLeft: (
<Button transparent onPress={() => this.props.navigation.toggleDrawer()}>
<Icon name='menu' style={{color: '#FFF'}} />
</Button>
),
headerRight: (
<HomeIcon navigation={navigation} />
),
headerStyle: {
backgroundColor: '#b80003'
},
headerTintColor: '#FFF'
}
}
});
const DrawerNavigator = createDrawerNavigator (
{
'Principal': {
screen: HomeNavigator
},
'Sobre o Aplicativo': {
screen: InformationApp
},
'Sobre os Responsáveis': {
screen: Team
},
'Sobre o Projeto': {
screen: Project
},
'PolĂtica e Termos': {
screen: Policy
}
},
DrawerConfig
);
const AppDrawerContainer = createAppContainer(DrawerNavigator);
export default AppDrawerContainer;
It is important to note that for screens belonging to const DrawerNavigator = createDrawerNavigator I am using the same code above to render drawerMenuButton and it is working normally, the error occurs only on const screens HomeNavigator = createStackNavigator.
Change your code like this
onPress={() => navigation.toggleDrawer()}>
I'm a beginner programmer and I'm trying to created a nested navigator in react native. The stack navigator on the Home screen works but the stack navigator on the MyEvents Screen does not. I use the tabs to navigate between Home and MyEvents and within the those pages I want to use a stack navigator to get to other pages. Please help! (also please let me know if I am being unclear)
This is Router.js
import React from 'react';
import {StackNavigator, TabNavigator} from 'react-navigation';
import InputScreen from './InputPage';
import HomeScreen from './Home';
import DetailsScreen from './Details';
import MyEventsScreen from './MyEvents';
import EditScreen from './EditPage';
import MapScreen from './Map';
const BottomNavigation = require('react-native-bottom-navigation');
export const Tabs = TabNavigator({
Home: {screen: HomeScreen},
EventMap: {screen: MapScreen},
Input: {screen: InputScreen},
MyEvents:{screen: MyEventsScreen},
},{
tabBarOptions: {
activeTintColors: '#e91e63',
//swipeEnabled: true,
}
});
export const EventsStack = StackNavigator({
Home: {
screen: Tabs,
},
Details: {
screen: DetailsScreen,
navigationOptions: {
header: null,
},
},
});
export const MyEventsStack = StackNavigator({
MyEvents: {
screen: Tabs,
navigationOptions: {
header: null
}
},
MyEventsDetails: {
screen: EditScreen,
navigationOptions: {
header: null,
/*navigation: ({ navigation }) => ({
title: `${navigation.state.params.name.first.toUpperCase()} ${navigation.state.params.name.last.toUpperCase()}`,
}),*/
},
},
});
export const Root = StackNavigator({
TabNav: {
screen: EventsStack,
}
}, {headerMode: 'none'});
This is Home.js
import React, {Component} from 'react';
import {View, Text, TouchableHighlight, SectionList} from 'react-native';
import {ListItem} from 'react-native-elements';
import {firebaseApp} from './App';
import Icon from 'react-native-vector-icons/MaterialIcons';
import {StackNavigator, TabNavigator} from 'react-navigation';
import {Tabs} from './Router';
export default class HomeScreen extends Component {
constructor(props) {
super(props);
this.state = {
data: []
};
this.itemsRef = firebaseApp.database().ref().child('items');
console.ignoredYellowBox = [
'Setting a timer'
];
}
onLearnMore = (item) => {
this.props.navigation.navigate('Details', {
...item
});
};
static navigationOptions = {
tabBarLabel: 'Home',
tabBarIcon: ({tintColor}) => (
<Icon
name = {'Home'}
size = {26}
style = {{color: tintColor}} />
)
}
listenForItems(itemsRef) {
itemsRef.on('value', (snap) => {
// get children as an array
var items = [];
snap.forEach((parent) => {
var children = [];
parent.forEach((child) => {
children.push({
"name": child.val().name,
"when": child.val().when,
"who": child.val().who,
});
});
items.push({
data: children,
key: parent.key.toUpperCase(),
})
});
this.setState({
data: items
});
});
}
componentDidMount() {
this.listenForItems(this.itemsRef);
}
render() {
var styles = require('./Styles');
const {navigate} = this.props.navigation;
return (
<View style={{flex: 1}}>
<View style={styles.header}>
<Text style={styles.title}>Princeton Events</Text>
</View>
<View style={styles.body}>
<SectionList renderItem={({item}) => <ListItem style={styles.item}
title={item.name} subtitle={item.when}
onPress={() => this.onLearnMore(item)}/>}
renderSectionHeader={({section}) =>
<Text style={styles.sectionHeader}>{section.key}</Text>}
sections={this.state.data} keyExtractor={(item) => item.name}/>
</View>
</View>
);
}
}
This is MyEvents.js
import React, {Component} from 'react';
import {View, Text, TouchableHighlight, FlatList} from 'react-native';
import {ListItem, List} from 'react-native-elements';
import Icon from 'react-native-vector-icons/MaterialIcons';
import Router from './Router';
export default class MyEventsScreen extends Component {
onViewMyEvent = (item) => {
this.props.navigation.navigate('Edit', {
...item
});
};
static navigationOptions = {
tabBarLabel: 'MyEvents',
tabBarIcon: ({tintColor}) => (
<Icon
name = {'account circle'}
size = {26}
style = {{color: tintColor}} />
)
}
render() {
var styles = require('./Styles');
const {navigate} = this.props.navigation;
return (
<View style={{
flex: 1
}}>
<View style={styles.header}>
<Text style={styles.title}>My Events</Text>
</View>
<View style={styles.body}>
<List containerStyle={{
borderTopWidth: 0,
borderBottomWidth: 0
}}>
<FlatList data={[
{
"name": "Event 1",
"who": "E-Club",
"what": "description goes here description goes here description goes here",
"when": "1:00",
"where": "Ehub",
"RSVP": "yes"
}, {
"name": "Event 2",
"who": "Club 2",
"what": "description goes here",
"when": "2:00",
"where": "Location 2",
"RSVP": "no"
}
]} renderItem={({item}) => <ListItem style={styles.item} title={item.name} subtitle={item.when} containerStyle={{
borderBottomWidth: 0
}} onPress={() => this.onViewMyEvent(item)}/>} keyExtractor={(item, index) => index}/>
</List>
</View>
<View style={styles.footer}>
<TouchableHighlight style={styles.button} onPress={() => navigate('Home')} underlayColor='#ffd199'>
<Text style={styles.buttonText}>Back</Text>
</TouchableHighlight>
</View>
</View>
);
}
}