How to render a modal on top of a screen - react-native

I am implementing firebase cloud messaging in my react native app. I have a regular class (not a react component) with static methods and variables that is instantiated when app fires. This class have three static methods/listeners (foreground, background and launch app) to manage push notifications from firebase.
I am trying to handle actions when in foreground.
How do I render a modal on top of a screen (not switching screens but run an overlay card in the center with two buttons), when user receives a push message from firebase, regardless of which screen he is on?
Tried solution provided on this article at hackernoon, wrapping modal component inside the AppContainer did not do anything. https://hackernoon.com/managing-react-modals-with-singleton-component-design-5efdd317295b
I have also tried the idea on this tutorial with modal component, but have not succeed in any cases.
https://reactnavigation.org/docs/en/navigating-without-navigation-prop.html
Firebase cloud messaging class:
export default class FCM {
static appClosedListener = async () => {
const firebaseContent = await firebase.notifications().getInitialNotification();
if (firebaseContent) {
console.log(firebaseContent);
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "closedapp";
FCM.pushNotificationHandler(serverParcel, notificationState);
}
};
static backgroundListener = () => {
FCM.background = firebase.notifications().onNotificationOpened(firebaseContent => {
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "background";
FCM.pushNotificationHandler(serverParcel, notificationState);
});
};
static foregroundListener = () => {
FCM.foreground = firebase.notifications().onNotification(firebaseContent => {
const serverParcel = JSON.parse(firebaseContent._data.payload).an;
const notificationState = "foreground";
FCM.pushNotificationHandler(serverParcel, notificationState);
});
};
static pushNotificationHandler = (serverParcel, notificationState) => {
const typeOfAction = serverParcel.typeEnum;
switch(typeOfAction){
case "message":
break;
case "webnews":
const url = serverParcel.link;
NavigationService.navigate("FCMModal", {}); //Here is where I want the modal to popup.
//NavigationService.navigate("InAppBrowser", {url});
break;
}
}
App.js
const ModalStack = createStackNavigator(
{
FCMModal: FCMModal
},
{
mode: 'modal',
transparentCard: true,
cardStyle: {
// makes transparentCard work for android
opacity: 1.0
}
}
)
let rootStack = createStackNavigator(
{
main: {
screen: BottomTabNavigator //There are four navigatorstacks with multiple screens beneath here
},
InAppBrowser: {
screen: InAppBrowserStack,
defaultNavigationOptions: {
tabBarVisible: true
}
},
FCMModal: {
screen: ModalStack
}
},
{
initialRouteName: "main",
headerMode: "none"
}
);
const AppContainer = createAppContainer(rootStack);
export default class App extends Component {
render() {
return (
<AppContainer
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
/>
);
}
}
Modal class
import React, { Component } from 'react';
import { Modal, Text, TouchableHighlight, View } from 'react-native';
export default class FCMModal extends Component {
state = {
modalVisible: true,
};
setModalVisible(visible) {
this.setState({modalVisible: visible});
}
render() {
return (
<View style={{width: 600, height: 400, borderWidth: 2, borderColor: 'red', opacity: 0.5}}>
<Modal
animationType="slide"
transparent={true}
visible={this.state.modalVisible}
onRequestClose={() => {console.log("CLOSING MODAL!")}}>
<View style={{marginTop: 22}}>
<View>
<Text>Hello World!</Text>
<TouchableHighlight
onPress={() => {
this.setModalVisible(false);
}}>
<Text>Hide Modal</Text>
</TouchableHighlight>
</View>
</View>
</Modal>
</View>
);
}
}

Related

'TypeError: Cannot read property 'state' of undefined' error' in react-native

I have created the header using defaultNavigationOptions The navigation bar contains Home, signup, login, create blog options. If the user has signed in then Login option should not be visible, instead, logout option should be enabled. I'm using AsyncStorage to store the token.
App.js:
import React from 'react';
import { CreateBlog } from './app/views/CreateBlog.js';
import { BlogDetails } from './app/views/BlogDetails.js';
import { EditBlog } from './app/views/EditBlog.js';
import { DeleteBlog } from './app/views/DeleteBlog.js';
import { createAppContainer } from 'react-navigation';
import { createStackNavigator } from 'react-navigation-stack';
import { Home } from './app/views/Home.js';
import { Login } from './app/views/Login.js';
import {
StyleSheet, Text, View, TextInput, TouchableHighlight, Alert, AsyncStorage
} from 'react-native';
import { Signup } from './app/views/Signup.js';
const AppNavigator = createStackNavigator(
{
CreateBlogRT: {
screen: CreateBlog
},
BlogDetailsRT: {
screen: BlogDetails
},
EditBlogRT: {
screen: EditBlog
},
DeleteBlogRT: {
screen: DeleteBlog
},
SignupRT: {
screen: Signup
},
LoginRT: {
screen: Login
},
HomeRT: {
screen: Home,
},
},
{
initialRouteName: 'HomeRT',
defaultNavigationOptions: ({ navigation }) => ({
header: <View style={{
flexDirection: "row",
height: 80,
backgroundColor: '#f4511e', fontWeight: 'bold'
}} >
<TouchableHighlight onPress={() => navigation.navigate('SignupRT')}>
<Text > SignUp</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => navigation.navigate('CreateChapterRT')}>
<Text > CreateChapter</Text>
</TouchableHighlight>
<TouchableHighlight onPress={() => navigation.navigate('LoginRT')}>
<Text > Login</Text>
</TouchableHighlight>
//getting 'TypeError: Cannot read property 'state' of undefined'
{this.state.logged_in &&
<TouchableHighlight onPress={async () => {
await AsyncStorage.removeItem('token');
await AsyncStorage.removeItem('user_id');
}}>
<Text > Logout</Text>
</TouchableHighlight>
}
</View >
}),
},
}
);
const MyRoutes = createAppContainer(AppNavigator);
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = { logged_in: false };
}
componentDidMount = async () => {
var logged_in = await AsyncStorage.getItem('token')
this.setState({ logged_in: false })
}
render() {
return (
<MyRoutes />
);
}
}
Now on clicking the logout button, it deletes the token. I want the logout button should be visible only if the user has logged in. In the same way login & signup options as well. (
const loggedin=AsyncStorage.getItem('token');if(userloggedin)
{showlogout:true;showlogin:false;showSignup:false}.
I don't know where to write the code. Thanks in advance.
after sign-in user you can set userloggedin true
AsyncStorage.setItem('userloggedin',JSON.stringfy(true)
or
AsyncStorage.setItem('userloggedin',Boolean(true))
then getItem
const logged-in = AsyncStorage.getItem('userloggedin')
if(loggedin){
showlogout=true
showlogin=false
showSignup=false
or if your are using state
this.setState({
showlogout:true,
showlogin:false,
showSignup:false})
}.
you can use react life-cycle for this work and also use global variable or state , like this
check this image
https://camo.githubusercontent.com/3beddc08b0e4b44fbdcef20809484f9044e091a2/68747470733a2f2f63646e2d696d616765732d312e6d656469756d2e636f6d2f6d61782f323430302f312a736e2d66746f7770305f565652626555414645434d412e706e67
class Clock extends React.Component {
constructor(props) {
super(props);
this.state = {logged_in: false};
}
componentDidMount=async() {
var logged_in = await AsyncStorage.getItem('token')
this.setState({logged_in: false})
}
render() {
const {logged_in} = this.state
return (
<div>
<h1>Hello, world!</h1>
<h2>It is {logged_in}.</h2>
</div>
);
}
}

How to set initial state empty when try to navigation to other page react native

I have a problem when i try to navigation.navigate called RegisterPage but before that i try to typing anycharacter in field email and after that i click register button and back again, but the field is not empty
LoginContainer.js
import React, { Component, Fragment } from 'react';
import LoginComponent from '../../modules/LoginComponent/component/LoginComponent';
import axios from 'axios';
import { Toast } from '#ant-design/react-native';
class LoginContainer extends Component {
static navigationOptions = {
header: null,
}
constructor(props) {
super(props);
this.state = {
post : [],
email : '',
password : '' ,
showPassword: true,
}
}
onValueChange = (text, name) => {
this.setState({
[name] : text
});
}
getPostAPI = () => {
axios.get('http://10.2.62.212:3000/dataadmin')
.then((res) => {
this.setState ({
post : res.data,
})
})
.catch((err) => {
})
}
showPassword = () => {
const showPassword = !this.state.showPassword
this.setState({ showPassword });
}
onLoginPress = (event) => {
event.preventDefault();
const { post } = this.state;
if(!this.state.email.trim()) {
Toast.fail('Invalid Email', 1, undefined, false)
} else
if(!this.state.password.trim()) {
Toast.fail('Invalid Password', 1, undefined, false)
} else
if (post.find(e => `${e.email}${e.password}` === `${this.state.email}${this.state.password}` )) {
this.props.navigation.navigate('Dashboard');
} else
if (post.find(e => `${e.email}${e.password}` !== `${this.state.email}${this.state.password}` )) {
Toast.fail('Invalid Email or Password', 1, undefined, false)
}
}
onRegistPress = () => {
this.props.navigation.navigate('Register')
}
componentDidMount() {
this.getPostAPI();
}
render() {
return (
<Fragment>
<LoginComponent
navigation = {this.props.navigation}
change = {this.props.change}
onValueChange={this.onValueChange}
showPassword={this.showPassword}
onLoginPress={this.onLoginPress}
showPassword={this.state.showPassword}
onRegistPress = {this.onRegistPress}
/>
</Fragment>
);
}
}
export default LoginContainer;
LoginComponent.js
import React from 'react';
import { View, Text, TextInput, TouchableOpacity } from 'react-native';
import PropTypes from 'prop-types';
import { Button, Icon, Provider } from '#ant-design/react-native';
import styles from '../../../../assets/styles/default.style'
class LoginComponent extends React.Component {
static navigationOptions = {
header: null,
}
render() {
return (
<Provider >
<View style={styles.containerLogin}>
<View style={styles.parentViewStyleLogin}>
<Text style={styles.textHeaderStyle}> Merchant APP </Text>
<Text style={styles.textStyle}>Email</Text>
<View style={styles.inputContainer}>
<Icon style = {styles.styleIcon} name={"user"} color='black'/>
<TextInput
style={styles.textboxfield}
underlineColorAndroid='transparent'
onChangeText={(text) => this.props.onValueChange(text, 'email')}
placeholder = {'Input your email here'}
placeholderTextColor={'rgba(221,221,221,1)'}
/>
</View>
<Text style={[styles.textStyle, {marginTop: 20}] }> Password </Text>
<View style={styles.inputContainer}>
<Icon style = {styles.styleIcon} name={"key"} color='black'/>
<TextInput
style={styles.textboxfield}
secureTextEntry={this.props.showPassword}
underlineColorAndroid='transparent'
onChangeText={(text) => this.props.onValueChange(text, 'password')}
returnKeyType={'done'}
placeholder = {'Input your password here'}
placeholderTextColor={'rgba(221,221,221,1)'}
/>
</View>
<TouchableOpacity >
<Button style={[styles.buttonTextStyle, { borderRadius : 30 }]} onPress = {this.props.onLoginPress} >Login</Button>
</TouchableOpacity>
<TouchableOpacity onPress = {() => this.props.onRegistPress()} >
<Text style={styles.textDefault} >Dont have account ? Register</Text>
</TouchableOpacity>
</View>
</View>
</Provider>
);
}
}
export default LoginComponent
navigation
import { createStackNavigator } from 'react-navigation-stack';
import { createAppContainer } from 'react-navigation';
import LoginContainer from '../../app/LoginContainer/LoginContainer';
import DashboardContainer from '../../app/DashboardContainer/DashboardContainer';
import NotificationContainer from '../../app/NotificationContainer/NotificationContainer';
import TransactionContainer from '../../app/TransactionContainer/TransactionContainer'
import RegisterContainer from '../../app/RegisterContainer/RegisterContainer'
import DetailContainer from '../../app/DetailContainer/DetailContainer';
const AppNavigator = createStackNavigator(
{
Login: { screen: LoginContainer },
Dashboard: { screen: DashboardContainer },
Notification: { screen: NotificationContainer },
Transaction : { screen: TransactionContainer },
Register : { screen : RegisterContainer },
Detail : { screen : DetailContainer }
},
);
export default createAppContainer(AppNavigator);
this my ui
look after i try to type anycharacter and then directly click signup , and then click sigin again , and the field is not empty. i dont know how to fix it
there are 2 ways you can achieve this :
While you click on register in the DOnt have account text , then before navigation.navigate you can set the state of email to '' , like
onRegisterClick = () =>{
this.setSTate({email:''});//first this
this.props.navigation.navigate('Register');//then this
}
You can go back to the login page again by this.props.navigation.push('Login') rahther than navigation.navigate coz what navigate does is it calls the page from existing stack ,and push creates a new stack so values will be reset , so email will be null

#expo/vector-icons, how to load font using react-navigation

I am trying to use this with react-navigation and I keep getting:
fontFamily 'Feather' is not a system font and has not been loaded
through Expo.Font.loadAsync
I am using the Font.loadAsync() method in my loading component but the error keeps showing up. I actually ended up removing all vector icons from my app in order to start clean.
So my question is if I have a switch navigator which starts with a loading screen (this starts the auth check) and switches either to login screen or root tab container which contains the meat of my app. I cant seem to figure out where to call Font.loadAsync()
Here is my MainNavigator.js
export default createBottomTabNavigator(
{
Friends: FriendsList,
Profile: Profile,
Settings: Settings
},
{
defaultNavigationOptions: ({navigation}) => ({
tabBarIcon: ({tintColor}) => {
const {routeName} = navigation.state;
let iconName;
if (routeName == "Friends") {
iconName = "users"
} else if (routeName == "Profile") {
iconName = "user"
} else if (routeName == "Settings") {
iconName = "settings"
}
return <Feather name={iconName} size={20} color={tintColor} />
}
}),
tabBarOptions: {
style: {
backgroundColor: "#fff"
},
inactiveTintColor: "#999",
activeTintColor: TERTIARY_COLOR
}
}
)
App.js
// Screens for Switch Navigator
import AuthScreens from "./components/screens/AuthScreens";
import LoadingScreen from "./components/screens/Loading";
import MainNavigator from "./MainNavigator";
const AppContainer = createAppContainer(createSwitchNavigator(
{
LoadingScreen,
AuthScreens,
MainNavigator
},
{
initialRouteName: "LoadingScreen"
}
))
Loading Screen
export default class LoadingScreen extends Component {
async componentDidMount() {
try{
await Font.loadAsync({
FeatherFont
})
firebase.auth().onAuthStateChanged(user => {
this.props.navigation.navigate(user ? "MainContainer" : "LoginScreen")
})
} catch(error) {
alert(error)
}
}
render() {
return (
<View style={{ flex: 1, justifyContent: "center", alignItems: "center"}}>
<ActivityIndicator/>
</View>
)
}
}
You need to load them in the App.js inside _loadResourcesAsync unsing the Font.loadAsync()
_loadResourcesAsync = async () => {
// blablabla
return Promise.all([
Asset.loadAsync([
require('random images'),
]),
Font.loadAsync(
// this line is where you load the font, name and path
{Feather: require('./assets/fonts/Feather-path.ttf')},
),
]);
};
And then inside the render function :
render() {
if (!this.state.isLoadingComplete && !this.props.skipLoadingScreen) {
return (
<AppLoading
startAsync={this._loadResourcesAsync}
onError={this._handleLoadingError}
onFinish={this._handleFinishLoading}
/>
);
} else {
return (
<View style={styles.container}>
<StatusBar barStyle="light-content" />
<AppNavigator style={styles.container} />
<BarraOffline connected={this.state.isConnected}/>
</View>
);
}
}
I don't know your definition of FeatherFont,
but I don't think Font recognize it as a font.
Font is the module that deals with font-related tasks.
First, we must load the font from our assets directory using
Expo.Font.loadAsync().
We can do this in the componentDidMount() lifecycle method of the
App component.
Add the following method in App:
Now that we have the font files saved to disk and the Font SDK
imported, let's add this code:
We need a way to re-render the Text component when the font has finished loading.
First we initialize fontLoaded to false in the App class
constructor:
class App extends React.Component {
state = {
fontLoaded: false,
};
async componentDidMount() {
await Font.loadAsync({
'open-sans-bold': require('./assets/fonts/OpenSans-Bold.ttf'),
});
this.setState({ fontLoaded: true });
}
}
With React Native you specify fonts in Text components using the fontFamily style property. The fontFamily is the key that we used with Font.loadAsync.
Usage
<Text style={{ fontFamily: 'open-sans-bold', fontSize: 56 }}>
Hello, world!
</Text>

Emitting one event cause other event to trigger in react native

Context:
I am rendering a web view and the navigation buttons are on top bar. I send an event when a navigation back button is pressed. And similarly with the forward button it fires an event. The problem is when I press the back button it causes forward button to fire as well. Thus on the console it says, back button pressed and forward button pressed. This behaviour happens in Android only, in ios it works perfect. I am not sure what am I missing in the Android side.
My component is as follows.
import _EventEmitter from 'EventEmitter'
const appEventEmitter = new _EventEmitter()
export { appEventEmitter }
import React, { Component } from 'react'
import {
StyleSheet,
ScrollView,
} from 'react-native'
import { connect } from 'react-redux'
import { WebView } from 'react-native-webview'
import { Linking } from 'react-native'
import Spinner from 'react-native-loading-spinner-overlay'
import { appEventEmitter } from 'src/common'
import { Icon } from 'react-native-elements'
const goBack = 'goBack'
const goForward = 'goForward'
class HomeComponent extends Component {
static navigationOptions = ({navigation}) => {
return {
headerRight:(
<Icon
iconStyle={styles.chevronColor}
name="chevron-right"
onPress={() => appEventEmitter.emit(goForward) }
size={40}
/>),
headerLeft:(
<Icon
iconStyle={styles.chevronColor}
name="chevron-left"
onPress={() => appEventEmitter.emit(goBack) } // This causes to fire back and forward events
size={40}
/>),
}
}
constructor(props) {
super(props);
this.state = {
webViewRef: "webViewRef",
visible: true,
}
}
componentDidMount () {
this.goBackListenerId = appEventEmitter.addListener(goBack, () => this.goBack())
this.goForwardListenerId = appEventEmitter.addListener(goForward, () => this.goForward())
}
componentWillUnmount () {
appEventEmitter.removeListener(this.goBackListenerId)
appEventEmitter.removeListener(this.goForwardListenerId)
}
goBack = () => {
console.log("BACK PRESSED")
this.refs[this.state.webViewRef].goBack();
}
goForward = () => {
console.log("Forward PRESSED")
this.refs[this.state.webViewRef].goForward();
}
hideSpinner() {
this.setState({ visible: false });
}
showSpinner() {
this.setState({ visible: true });
}
render() {
return (
<ScrollView contentContainerStyle={styles.scrollableContainer}>
<Spinner
visible={this.state.visible}
style={styles.spinnerColor}
/>
<WebView
source={{uri: BASE_URL}}
style={styles.container}
onLoadStart={() => this.showSpinner()}
onLoadEnd={() => this.hideSpinner()}
ref={this.state.webViewRef}
javaScriptEnabled={true}
domStorageEnabled={true}
geolocationEnabled={true}
cacheEnabled={true}
/>
</ScrollView>
)
}
}
const styles = StyleSheet.create({
scrollableContainer: {
flex: 1,
},
spinnerColor: {
color: 'white'
},
navigationHeader: {
backgroundColor: colors.primary,
},
container: {
flex: 1,
},
chevronColor: {
color: 'white'
}
});
const Home = connect()(HomeComponent)
export { Home }

How to access the react-navgiation inside of a functional component or class component which doesnt have access to this.props.navigation?

Im doing this inside the react native platform using expo.
I want to display the list of items ( ListItems.js) All_Employees_screen.js . These items are being rendered via a functional component, I want to have a onRowPress() handler to so that upon clicking it i can navigate it to another view, but I dont know how to do it on react-navigation ?
Or since the new functional component can be a class component( this would be better ) how can i access the navigation thing inside it ?
AllProperties.js
import _ from 'lodash';
import React, {
Component
} from 'react';
import {
Button,
ListView,
ScrollView
} from 'react-native';
import ListItem from './ListItem';
import { connect } from 'react-redux';
import { propertiesFetch } from '../../actions';
// import { FormLabel, FormInput } from 'react-native-elements'
class AllPropertiesScreen extends React.Component {
componentWillMount(){
this.props.propertiesFetch();
this.createDataSource(this.props);
}
// we do this componentWillMount & componentWillReceiveProps (nextProps) thing twice, coz once the component is
// loaded it loads all teh values but when user hits another view like Create property, The Property data still exists
// in the global state object,
// we could move all the dc dataSource code into componentWillReceiveProps but its actually gonna benefit us
// if we make sure that we try to build our data source both when the component first loads up
// & when second time after we go back and forth other compoennts.
componentWillReceiveProps(nextProps){
// nextProps are the next set of props that this component will be rendered with
// this.props is still the old set of props
this.createDataSource(nextProps);
}
createDataSource({ properties }){
const ds = new ListView.DataSource({
rowHasChanged: (r1, r2) => r1 !== r2
});
this.dataSource = ds.cloneWithRows(properties);
}
static navigationOptions = ({ navigation }) => {
const {state, setParams} = navigation;
return {
title: 'All Emplooyee',
headerRight: (
<Button
title='Add'
// onPress={() => setParams({ mode: isInfo ? 'none' : 'info'})}
onPress={() => navigation.navigate('createProperty')
}
/>
),
};
};
goBack(){
console.log('65 - go Back clicked');
}
renderRow(property){
// console.log('67-AllPropertiesScreen =', property);
return <ListItem property={property}
onPress={() => { console.log('65 - go Back clicked') }}
/>;
}
render() {
console.log('72-AllPropertiesScreen this.props', this.props );
return(
<ListView
enableEmptySections
dataSource={this.dataSource}
renderRow={this.renderRow}
/>
);
}
}
const mapStateToProps = state => {
console.log('83 - AllPropertiesScreen state. properties', state );
const properties = _.map(state.properties, (val, uid ) => {
return { ...val, uid }; // { shift: 'Monday'}
});
return { properties };
};
export default connect(mapStateToProps, {propertiesFetch}) (AllPropertiesScreen);
ListItem.js
import React, { Component } from 'react';
import { Text, TouchableWithoutFeedback, View } from 'react-native';
class ListItem extends Component {
// onRowPress(){
// Actions.employeeEdit({ employee: this.props.employee });
// }
render(){
const { agent_name, cell, address } = this.props.property;
console.log('14- ListItem ', this.props);
return (
<View>
<CardSection>
<Text style={styles.titleStyle}>
name
</Text>
<Text style={styles.titleStyle}>
cell
</Text>
<Text style={styles.titleStyle}>
address
</Text>
</CardSection>
</View>
);
}
}
const styles = {
titleStyle: {
fontSize: 18,
paddingLeft: 15
}
}
export default ListItem;
//
main.js ( this is where I have all the navigation paths hookedup.
class App extends React.Component {
render() {
const MainNavigator = TabNavigator({
// auth: { screen : AuthScreen },
// review: { screen: ReviewScreen },
// signup: { screen : SignupScreen },
followup: { screen: FollowupScreen }, welcome: { screen : WelcomeScreen },
auth: { screen : AuthScreen },
signup: { screen : SignupScreen },
main: {
screen: TabNavigator ({
followup: { screen: FollowupScreen },
map: { screen: MapScreen },
deck: { screen: DeckScreen },
settings : {
screen: StackNavigator ({
settings: { screen: SettingsScreen },
// settings: { screen: SettingsScreen },
UserProfile: { screen: UserProfileScreen },
HelpSupport: { screen: HelpSupportScreen },
Notifications: { screen: NotificationsScreen },
Signout: { screen: SignoutScreen } // not working, Navigation object not accessible inside the component
}) //screen: StackNavigator ({
},
followup : {
screen: StackNavigator ({
followup: { screen: FollowupScreen },
allProperties: { screen: AllPropertiesScreen },
createProperty: { screen: PropertyCreateScreen },
Red: { screen: RedPriorityScreen }, // not working, Navigation object not accessible inside the component
GreyPriority: { screen: GreyPriorityScreen },
}) //screen: StackNavigator ({
},
draw: {
screen: DrawerNavigator ({
drawin: { screen: DrawScreen },
}) //screen: StackNavigator ({
}
}) //screen: TabNavigator
}
}, {
navigationOptions: {
tabBarVisible: false
},
lazy: true
});
return (
<Provider store={store}>
<View style={styles.container}>
<MainNavigator />
</View>
</Provider>
);
}
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
// alignItems: 'center',
justifyContent: 'center',
},
});
Expo.registerRootComponent(App);
Solution suggested by #Matt but as soon as I put the navigation={this.props.navigation} it complains. undefined is not an object ( evaluating this.props.navigation )
renderRow(property){
return (
<ListItem
property={property}
navigation={this.props.navigation}
onPress={() => {
console.log( '70-on Press inside renderRow ');
}}/>
);
}
If the component is not a screen you have to import the navigation.
Try this:
import React from 'react';
import { Button } 'react-native';
import { withNavigation } from 'react-navigation';
class MyBackButton extends React.Component {
render() {
return <Button title="Back" onPress={() => { this.props.navigation.goBack() }} />;
}
}
// withNavigation returns a component that wraps MyBackButton and passes in the
// navigation prop
export default withNavigation(MyBackButton);
For more info check out
https://reactnavigation.org/docs/connecting-navigation-prop.html
This answer was written for old version of react-navigation V1
I had the same exact problem, and I found out that this.props.navigation is injected only in components that are registered as screen in StackNavigator or TabbNavigator.
but in general you can use navigate from NavigationActions class (source here https://v1.reactnavigation.org/docs/navigation-actions.html#navigate)
note: NavigationActions.navigate receives parameters in different way but works the same way.
so this working for me
import { NavigationActions } from 'react-navigation';
let {navigate} = NavigationActions;
renderRow(property) {
return (
<ListItem
property={property}
onPress={() => { navigate({
routeName: 'OtherRoute'
});
}}/>
);
}
<MyComponent navigation={this.props.navigation}/>
Main problem is here. You didn't define your prop navigation in component. You should add this.
Here's how you can use navigation.navigate inside a functional component:
import { Text, TouchableHighlight } from 'react-native';
const MyComponent = ({ navigation }) => (
<TouchableHighlight
onPress={() => navigation.navigate('OtherRoute')}
underlayColor="blue"/>
<Text>Click to Navigate!</Text>
</TouchableHighlight>
);
export default MyComponent;
When you render MyComponent, you will need to pass navigation as a prop. For example, assume HomeContainer is a screen component:
import React from 'react';
import MyComponent from './MyComponent';
export default HomeContainer extends React.Component {
render() {
return (
<MyComponent navigation={this.props.navigation}/>
);
}
}
Change your renderRow method to the following:
renderRow(property) {
return (
<ListItem
property={property}
onPress={() => { this.props.navigation.navigate('OtherRoute'); }}/>
);
}
where 'OtherRoute' is the name of the route you want to navigate to for that row.