React-Navigation added to existing apps - android

Some background for context...I'm creating a react-native app that will live in it's own repository. This app will be integrated into other web, iOS, and Android apps. I'm currently working with the Android app. This is an existing native app written in Java.
I've "wrapped" it with a super basic React Native app, following the React Native Docs. For testing purposes, to mimic what will end up being the production app, I have a native activity, MainActivity in the Android app, with a button that opens the second activity, ReactNativeActivity which is loads my React Native app. This is working - the React Native app loads. The React Native App has a button that should navigate to a second screen within the React Native app (using react-navigation). When I press that button, instead of navigating to the next screen in my stack, it instead reloads MainActivity from the native Android app. Running the same thing on web there is no issue with navigation, so I know the screens are hooked up right. I'm not new to React Native, but I am new to integrating with existing Native applications.
Edited to add some code as requested
The Navigator and the button are as follows:
// navigator.tsx
import React from "react";
import { NavigationContainer } from "#react-navigation/native";
import { createNativeStackNavigator } from "#react-navigation/native-stack";
import { HomeScreen } from "../screens/Home";
import Demo from "../screens/Demo";
export type NavigationStackParamList = {
HomeScreen: undefined;
Demo: undefined;
};
const Stack = createNativeStackNavigator<NavigationStackParamList>();
export const Navigation = () => {
return (
<NavigationContainer>
<Stack.Navigator>
<Stack.Screen component={Demo} name="Demo" />
<Stack.Screen
component={HomeScreen}
name="HomeScreen"
options={{ title: "Home" }}
/>
</Stack.Navigator>
</NavigationContainer>
);
};
// Demo Screen
const Demo: React.FC<DemoScreenProps> = ({ navigation }) => {
return (
<View style={styles.app}>
<View style={styles.info}>
<Text style={styles.title}>Demo</Text>
<Text>Running on: </Text>
<Text style={styles.platform}>{Platform.OS}</Text>
</View>
<Map />
<Button
onPress={() => navigation.navigate("HomeScreen")}
title="Go to Test Screen"
/>
</View>
);
};
Pressing "Go to Test Screen" above, is what is causing the MainActivity to reload, rather than the correct screen.

Related

Error: Couldn't find a navigation object. Is your component inside NavigationContainer? - React Native

Working thru a React Native tutorial and got to a part on Routing between various components. It looks like it doesnt like the "Menu" component and its asking, as the title says,
Is your component inside NavigationContainer?
As far as I can tell yes. I tried asking the author but this was on Pluralsight and I dont't think they've checked back in ages. I checked their code though, and it works just like I have it. So not sure what may have changed with the framework since that series was made.
Here is the full output when the error pops:
This error is located at:
in Menu (created by Home)
in RCTView (created by View)
in View (created by Home)
in RCTView (created by View)
in View (created by Home)
in Home (created by App)
in App
in RCTView (created by View)
in View (created by AppContainer)
in RCTView (created by View)
in View (created by AppContainer)
in AppContainer
in GloboTicket(RootComponent), js engine: hermes
The Menu component (minus StyleSheet)
import React from 'react';
import {View, TouchableOpacity, Text, StyleSheet} from 'react-native';
import {useNavigation} from '#react-navigation/native';
const Menu = () => {
const navigation = useNavigation();
return (
<View style={styles.menu}>
<TouchableOpacity
onPress={navigation.navigate('Tickets')}
style={styles.button}>
<Text style={styles.buttontext}>Events</Text>
</TouchableOpacity>
</View>
);
};
export default Menu;
Code from "Home" where the Menu is used (minus StyleSheet):
import React from 'react';
import {View, Image, Text, StyleSheet} from 'react-native';
import Menu from './Menu';
const Home = props => {
return (
<View style={styles.container}>
<Image
style={styles.globologo}
source={require('./images/globologo.png')}
/>
<Text style={styles.title}>Welcome to GloboTicket</Text>
<Text style={styles.subtitle}>{props.username}</Text>
<Image
style={styles.concertimage}
source={require('./images/concert.jpg')}
/>
<View style={styles.textcontainer}>
<Text style={styles.content}>{introText}</Text>
</View>
<View style={styles.menu}>
<Menu />
</View>
</View>
);
};
App file (where all lies within a NavigationContainer):
import 'react-native-gesture-handler';
import React from 'react';
import { StatusBar } from 'react-native';
import { NavigationContainer } from '#react-navigation/native';
import { createStackNavigator } from '#react-navigation/stack';
import Home from './Home';
import Tickets from './Tickets';
const Stack = createStackNavigator();
function App(): JSX.Element {
return (
<>
<StatusBar barStyle='dark-content' hidden />
<NavigationContainer>
<Stack.Navigator
initialRouteName='Home'
>
<Stack.Screen
name='Home'
options={{
headerShown: false,
}}
>
{props => <Home {...props} username='Bella' />}
</Stack.Screen>
<Stack.Screen
name='Tickets'
component={Tickets}
options={{
headerTitleAlign: 'center',
headerTitleStyle: { fontFamily: 'Ubuntu-Regular' }
}}
/>
</Stack.Navigator>
</NavigationContainer>
</>
);
}
export default App;
I have checked the documentation to make sure I set it correctly and that I wasnt doing something blatantly wrong, and I cant find anything suggesting that I did.
What I expected was for my code to not have an error I guess? Or at least for the error to be something that tells me what to do in more detail so I can fix it. There is an image that shows on the emulator where it throws the error, but its literally where the error is thrown and not what caused it.
Seen Here
I have seen people mention the below, but I seem to have this covered to my knowledge:
useNavigation only works if your component is inside of a NavigationContainer and a Navigator
I have also looked at this page, as multiple SO answers have suggested. I dont truly understand it, but from a surface level I dont think it applies if I AM navigating with navigation prop:
Link Here
I cant just get rid of the component, thats not a viable option. So I'm hoping someone here has some advice.
Stumbled into an answer. Not sure if it is THE answer but it worked. Detailed below:
In my Menu file, I had this:
onPress={navigation.navigate('Tickets')}
But I suppose what it wanted, or what ended up working at least was:
onPress={navigation.navigate(Tickets)}
Notice the lack of single quotes.
What I did was import the Tickets component to the Menu file and used that for the Navigate call.
UPDATE TO ADD METHOD #2
It's been a few days, but I found another answer. In my App file, you can see I named the components with single quotes. Changing them to double quotes fixed this as well, for me at least.
Example:
<Stack.Screen
name='Tickets' // CHANGE THIS TO name="Tickets"
component={Tickets}
options={{
headerTitleAlign: 'center',
headerTitleStyle: { fontFamily: 'Ubuntu-Regular' }
}}
/>

React-native WebView doesn't keep user logged in iOS devices

I've built a simple application that loads a page using Webview and, even though I've allowed cookies and cache, once the app is closed, the user is logged out from the website. PS.: This only happens in iOS devices, not in Android.
import React from 'react';
import WebView from 'react-native-webview';
import { useNavigation } from '#react-navigation/native';
const WebViewPage = () => {
const { navigate } = useNavigation();
return (
<WebView
source={{ uri: 'my-url' }}
onError={() => navigate('Error')}
thirdPartyCookiesEnabled
allowFileAccess
cacheEnabled
sharedCookiesEnabled
/>
);
};
export default WebViewPage;
I've found solutions for Flutter applications, but not for React-native.

React-native onPress() event doesn't work in Android

I am experiencing a very weird behaviour in my android emulator when using react-navigation v5 in my react native application, which makes me think is a bug. I found out that there is an open issue on the official react-native-navigation page. See here.
In every secondary screen (see UserProfileScreen below), in many react native-elements like TouchableOpacity Button or TextInput the onPress event doesn't work. It only works in the main screen of the navigator. (See HomeScreen below)
Here is an example of how I create my stack navigator:
import {createStackNavigator} from '#react-navigation/stack';
// some imports here
const HomeStackNavigator = createStackNavigator();
export const HomeNavigator = () => {
return (
<HomeStackNavigator.Navigator>
<HomeStackNavigator.Screen name="HomeScreen" component={HomeScreen}/>
<HomeStackNavigator.Screen name="UserProfileScreen" component={UserProfileScreen}/>
<HomeStackNavigator.Screen name="UserSettingsScreen" component={UserSettingsScreen}/>
</HomeStackNavigator.Navigator>
)
};
As a PoC I have the same code in the main and secondary screen:
<TouchableOpacity
onPress={() => Alert.alert("You pressed me!")} >
<Text> touch me</Text>
</TouchableOpacity>
and I see the alert only in the main screen (HomeScreen).
If I make UserProfileScreen as the fist screen in my stack navigator above then it works (the onPress event) fine on this screen but not in the HomeScreen. So it seems that the onPress event is triggered only in the main screen!. On IOS it works fine in all screens. Any idea ? Let me know if you need some more code snippets.

Error using StackNavigator with switching screens

I'm trying to make a SignUp/Login screen with a stackNavigator.
But when I try to navigate from the login page to the signup page.
I've tried creating a const that would be the navigate property but I get the same. If I do all my screens in the same file with the stackNavigator I don't get error but this is not really what I want to do.
Here is my Login Screen first (before getting to this screen in index.js I call the Welcome class to check if the user is logged in are not and I return the if he's not
export default class Login extends React.Component{
render(){
return(
<ScrollView style={{padding : 20}}>
<Text style={styles.title}>
Login
</Text>
<TextInput style={styles.input} placeholder="Email Address"/>
<TextInput style={styles.input} placeholder="Password" />
<View style={{margin : 7}}/>
<TouchableOpacity
onPress={() => this.props.navigation.navigate('Signup')}
>
<Text> Don't have an account? Click here</Text>
</TouchableOpacity>
<Button
onPress={this.props.onLoginPress}
title="Log in"
color='grey'
/>
</ScrollView>
);
}
}
My sign up screen is pretty empty it only returns a View with a title as of right now
And finally here's my StackNavigator file
const AppNavigator = createStackNavigator({
Welcome: {
screen : Welcome
} ,
Login: {
screen: Login
},
Signup : {
screen : Signup
},
},
{
initialRouteName: "Welcome"
});
export default createAppContainer(AppNavigator);
Where Welcome is the file that checked if user is logged in are not
The error I get is "error undefined is not an object (evaluating '_this.props.navigation.navigate')." When I click the in Login.js
checked your repo. Your welcome screen first checks if the user is logged in or not and if not logged in, it renders the login screen. You're not navigating from Welcome screen to Login screen, but rendering another Login screen. The problem lies here. I'll give an example:
In your phone, say iPhone X, you downloaded and installed Facebook from the App Store. Then you logged into your account. That means, the Facebook app in your phone is now configured to use your account. Now, if you want to post your photo, you wont be able to do it if you download and install another Facebook app and try to post from there. You will have to use the same old app which is configured to use your account. (Actually you won't be able to download two at a time :P)
In the same way, you should tell the stack navigator to navigate to the stack navigator's login screen, not render another login screen. The new login screen you created won't have the navigation prop, unless you pass one.
But there is another way. You can render the signup screen the same way you render your login screen. Copy this code to your Welcome.js:
import React from 'react';
import { Text, View, Button } from 'react-native';
import LoginScreen from './LoginScreen';
import SignupScreen from './Signup';
import DrawerNavigator from '../navigation/DrawerNavigator';
const notLoggedIn=0, loggedIn=1, signingUp=2;
export default class Welcome extends React.Component{
constructor(props){
super(props);
this.state={
state: notLoggedIn
}
}
render(){
switch (this.state.state){
case notLoggedIn:
return (
<LoginScreen
onLoginPress={()=>this.setState({state:loggedIn})}
onSignUpPress={()=>this.setState({state:signingUp})}
/>
);
case loggedIn:
return <DrawerNavigator />;
case signingUp:
return <SignupScreen />; //Add onBackPress={()=>this.setState({state:notLoggedIn})}} prop here if you want
}
}
}
Then in your Login.js, change onPress prop from this.props.navigation.navigate('Signup') to this.props.onSignUpPress().
You won't need the stack navigator now.(You've your own navigator now) So, instead of using that stack navigator, you can now directly use your Welcome screen. Now you can just delete the stacknavigator.js file.
This should work out, I guess.
Tip: Saw that you have many unused styles and imports in your files. If you're not really going to use them, you can just remove those lines. That's up to you.
EDIT:
To pass a function into a navigator, you can use the screenProps prop.
Edit your Welcome.js:
case loggedIn:
return <DrawerNavigator logout={()=>this.setState({state:notLoggedIn})} />;
DrawerNavigator.js:
...
const DrawerNavigator = createAppContainer(createDrawerNavigator(
...
));
export default class navigator extends React.Component {
render(){
return <DrawerNavigator screenProps={{logout: this.props.logout}} />
}
}
And inside your logout.js, call the function this.props.screenProps.logout() from wherever you want.
This should work.

React Native StackNavigator disappearing on re-entry

We are encountering a very bizarre scenario with react-navigation in our React Native application that is only observed on Android (both in the emulator and on physical devices AND for debug builds as well as release builds), but it works fine on iOS.
Context
We have an existing native application, and decided to implement some new screens in React Native as an experiment to see whether it would benefit our development lifecycle.
Our native app has a sidebar menu, and we added a new menu item, that when selected, takes the user into the React Native portion. They can of course navigate back out whenever they want, and later go back into that React Native portion.
Observed problem (Only occurs in Android)
We have identified it relates to the react-navigation library, but we don't know what we're doing wrong.
When the app is first loaded, the user can select the new menu item and the React Native app loads fine, showing its initial route page and with the StackNavigator working fine.
If the user returns to the native portion (either via the back key, or by selecting a different option from the sidebarmenu) and then later opts to return to the React Native portion, then the StackNavigator portion doesn't display. Other React components outside the StackNavigator get rendered. We know it mounts the contained components, as some of them are making API calls and we see those endpoints being queried. It just doesn't render.
Reloading within the emulator will render the app properly again until we navigate out of React Native and then return.
Oddly enough: If we turn on remote JS debugging, it suddenly all works fine.
So our question:
Can anyone spot what we might be missing in how we are using the StackNavigator, that is keeping it from rendering properly? Again: it works fine when the JS debugger is on, making us think that it is not a logic item, but perhaps a timing condition, or some subtle config? Or should we just ditch react-navigation and go to a different navigation library?
Simple reproduction of the issue
Our package.json is:
{
"dependencies": {
"react": "16.0.0",
"react-native": "0.50.4",
"react-navigation": "1.5.2"
}
}
Our React Native entry page (index.js) is:
import * as React from 'react';
import { AppRegistry, Text, View } from 'react-native';
import { StackNavigator } from 'react-navigation';
import TestPage from './TestPage';
AppRegistry.registerComponent('MyApp', () => MyApp);
class MyApp extends React.Component {
public render() {
return (
<View style={{flex:1}}>
<Text>'This text always shows up fine on Android, even on reentry to React application'</Text>
<AccountNavigator />
</View>
);
}
}
const AccountNavigator = StackNavigator(
{
FirstPage: {
screen: TestPage,
navigationOptions: ({ navigation }) => ({
title: 'Test View'
})
},
{
initialRouteName: 'FirstPage'
}
);
The simple test page (TestPage.js) is just:
import * as React from 'react';
import { Text, View } from 'react-native';
export default class TestPage extends React.Component {
render() {
return (
<View style={{flex:1, alignItems: 'center', justifyContent: 'center'}}>
<Text>'If you can read this, then the app is on first load. But return to the native portion and then try to come back to React Native and you will not see me.'</Text>
</View>
);
}
}
Turns out it was a layout setting issue. In our native code, within our React Activity layout XML we had:
<com.facebook.react.ReactRootView
android:id="#+id/ReactRootView
android:layout_width="match_parent"
android:layout_height="wrap_content" />
and the issue was in the "wrap_content" for height which was causing it to render the StackNavigator() as 1 pixel high. No idea why it always happened only on re-entry and not on the first time, nor why the JS debugger would cause the issue to disappear.
Changing layout_height to "match_parent" resolved the issue altogether.

Categories

Resources