react-navigation v2 Back Button Closes The App - android

I am facing a very weird issue with an app which has been working fine and after upgrading to react-navigation v2 has started to have the issue.
Anywhere within the app, the Back Button on Android closes the app and moves it back to the suspended apps.
I have tried many things in terms of handling the back behaviour manually, downgrading some of the packages etc but none of them worked.
Here is my package.json file:

I had the same issue and these are what I found:
https://github.com/react-navigation/react-navigation/issues/4329
and
https://github.com/expo/expo/issues/1786
A temporary solution is mentioned, which is to downgrade firebase to 5.0.3, which works for me.

The issue is with the npm firebase package. There are two ways to fix this.
As mentioned in the other answers, downgrade firebase to 5.0.3
Change the way in which you import firebase. This method will be a lot
easier.
Use:
import firebase from "#firebase/app";
import "firebase/auth";
import "firebase/database";
Don't use import * as firebase from "firebase"; or import firebase from "firebase";
See this GitHub issue for more details.

The solution of downgrading Firebase worked for me too but I had to downgrade to Firebase 4.13.1 as with 5.0.3 I was still facing the issue.

If you are using react-navigation v2, take a look this documentation.
You can also use react-navigation-backhandler for an easy-to-use solution.

Converting this import statement fixed my issue
import Firebase from '#firebase/app' // The issue got fixed after adding #
import 'firebase/auth'
import 'firebase/database'
import 'firebase/storage'
Without the # the backbutton was exiting the user from application

I have faced the same issue, it seems like the implementation of Backhandler.android.js is not correct, you can find the file here node_modules/react-native/Libraries/Utilities/BackHandler.android.js , in this file const subscriptions = Array.from(_backPressSubscriptions.values()).reverse(); piece of code always returns an array of length 0, that's why the invokeDefault variable always stays true and closes the app, you can fix it by handling the back button behavior via your own implementation.
In Navigation Service add this method
import { NavigationActions, StackActions } from 'react-navigation';*
let navigator;
function setTopLevelNavigator(navigatorRef) {
navigator = navigatorRef;
}
function pop() {
navigator.dispatch(StackActions.pop());
}
export default {
pop,
setTopLevelNavigator
};
You need to set the top level navigator in your app.js, like this int render method's return statement
<AppNavigator //Replace it with your navigator
ref={navigatorRef => {
NavigationService.setTopLevelNavigator(navigatorRef);
}}
onNavigationStateChange={(prevState, currentState) => {
this.setState({ currentState });
}}
/>
To handle the Back button functionality add these things in your app.js
import NavigationService also
import {
BackHandler,
DeviceEventEmitter
} from 'react-native';
In componentDidMount add these
componentDidMount() {
BackHandler.addEventListener('hardwareBackPress', this.handleHardwareBack);
}
In componentWillUnmount add these
componentWillUnmount() {
BackHandler.removeEventListener('hardwareBackPress',this.handleHardwareBack);
}
Now handling the hardware back button
handleHardwareBack = () => {
if (!isUndefined(this.state.currentState)) {
const mainRouteIndex = this.state.currentState.index;
const mainRoute = this.state.currentState.routes[mainRouteIndex];
const subRouteIndex = mainRoute.index;
if (subRouteIndex === 0) {
console.log(
'the screen name is ----> ',
mainRoute.routes[subRouteIndex].routeName
);
this.toggleExitModal(); //you can place any dialog if you want to show
return true;
}
NavigationService.pop();
return true;
}
console.log('Back Button is handled in the respective page seperately');
};
return true tell that we are going to handle the back button functionality manually, return false will lead to exits the app as by default it is Hardware back button closes the app :(
Hope this will help you

Well i am using firebase 5.5.5 i don't have any problem with navigation , I think you need to create your stack navigator to use the back butoon properly , I have given a example of it. pages are imported also i have not attached the screen importing code
import { createSwitchNavigator, createStackNavigator } from 'react-navigation';
const Drawer = createDrawerNavigator(
{
BrowseQuestion: BrowseQuestion,
BrowseCategory: BrowseCategory,
}
);
const Loginstack = createStackNavigator({
Login: LoginScreen,
Forgot: ForgotPassword,
Signup: SignupScreen,
})
export default createSwitchNavigator({
Login : Loginstack,
Account: Drawer,
},
{
initialRouteName: 'Login'
}
);

Related

How can I improve the launch time react native app?

I've already published an app for IOS and Android.
But the app makes launch time around 5 seconds.
I detect it to make around 5 seconds to load the first screen in Routes
The app.tsx code below:
import React, {useEffect} from 'react';
import configureStore from './Store';
import {SafeAreaProvider} from 'react-native-safe-area-context';
import {Provider} from 'react-redux';
import {PersistGate} from 'redux-persist/es/integration/react';
import {NavigationContainer} from '#react-navigation/native';
import Routes from './configs/Routes';
import {navigationRef} from './services/NavigationService';
import {LocalizationProvider} from './locales/Translation';
import ModalContainer from './modules/ModalGlobal/containers/ModalContainer';
import NetInfoContainer from './containers/NetInfoContainer';
import {
listenNotificationForeground,
requestUserPermission,
} from './services/NotificationService';
import axios from 'axios';
import {removeCurrentSession} from './modules/Setting/service/SettingService';
import {UNAUTHORIZED} from './configs/Constants';
axios.interceptors.response.use(
response => response,
error => {
const {status} = error.response;
if (status === UNAUTHORIZED) {
removeCurrentSession();
}
return Promise.reject(error);
},
); // User for HTTP code difirrent 200 from axios
const App = () => {
useEffect(() => {
let listenBackground: any;
async function _notificationHandle() {
await requestUserPermission();
listenBackground = listenNotificationForeground();
}
_notificationHandle();
return listenBackground;
}, []);
return (
<Provider store={configureStore().store}>
<PersistGate loading={null} persistor={configureStore().persistor}>
<SafeAreaProvider>
<LocalizationProvider> // Locale language
<NetInfoContainer /> // Use to show lost connection
<NavigationContainer ref={navigationRef}>
<Routes />
</NavigationContainer>
<ModalContainer /> // Define global modal to use many routes
</LocalizationProvider>
</SafeAreaProvider>
</PersistGate>
</Provider>
);
};
export default App;
Could I improve anything in this code? Or any way to improve launch time?
Thank you very much!
for android you can eliminate blank screen at the beginning of the project by enabling hermes on app/build.gradle. Hermes convert js code to java code. If you defector your release apk, you can see only java file after enabling hermes and there is a change to reduce the size of your application. the full instruction and documentation see this
You can use react-native-screens to optimize the memory usage and performance of your app, use it as follow (After installing it with yarn add react-native-screens):
Your android/app/src/main/java/<your package name>/MainActivity.java need to look like this:
import android.os.Bundle;
#Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(null);
}
On your entry file (Mostly App.js), add the following:
import { enableScreens } from 'react-native-screens';
enableScreens(false);
More about it here: Optimize memory usage and performance using react-native-screens
You should also add a splash-screen (if you don't have one) because there will always be a blank screen at the start of 1 second while react-native loads its dependencies. Setting up a splash screen will make this unnoticeable.
i use react-native-splash-screen

Ionic 4 Delete Page from History (Android)

Android devices has back button on menu toolbar. I want to disable the possibility when i login to my app and click on that back button to route on login page.
I want if user click on back button after login then i close the app.
Here is my initial code for routing below.
if (token) {
this.router.navigate(['/main-tabs/tabs/dashboard'])
} else {
this.router.navigate(['/login']).then();
}
I've tried many other answers but none of them really works for me. But this one works :
To disallow the login from going 'back' to the authenticated page after logged out, just do something like this in your app-routing.module.ts :
{
path: 'home',
loadChildren: './home/home.module#HomePageModule',
canActivate: [LoggedAuthGuard]
}
The same for the opposite (to prevent going back into login page with back button) :
{
path: 'login',
loadChildren: './login/login.module#LoginPageModule',
canActivate: [NotLoggedAuthGuard]
}
And both LoggedAuthGuard and NotLoggedAuthGuard must implement CanActivate. Sample code as below (with Promise, but it also works with boolean return) :
import { Injectable } from '#angular/core';
import {CanActivate} from "#angular/router";
import {Storage} from "#ionic/storage";
#Injectable({
providedIn: 'root'
})
export class LoggedAuthGuard implements CanActivate {
constructor(protected storage: Storage) { }
async canActivate() {
return (await !!this.storage.get('access_token'));
}
}
For the NotLoggedAuthGuard you just returns the opposite of LoggedAuthGuard.
async canActivate() {
return (await !this.storage.get('access_token'));
}
Hope this helps.
This answer provides a solution for removing the login page from the browser's history by replacing it with a page, that the user was navigated to after successful login. It might be a good and quick solution to:
I want to disable the possibility when i login to my app and click on
that back button to route on login page.
What I understood from your question is after user login, You don't want to navigate to login page if back button is clicked. If I understood your question correctly you can try below solution.
one approach is changing root page
this.navCtrl.setRoot(HomePage);
or
You can achieve this by removing page from stack after successful transition. Place below code inside Login success method
let currentIndex = this.navCtrl.getActive().index;
this.navCtrl.push(DestinationPage).then(() => {
this.navCtrl.remove(currentIndex);
});
Hope this helps you.
I think you can do like that :
this.platform.backButton.subscribe((()=>{
if(this.router.url == <insertpathhome>)
{
this.platform.exitApp();
}
else{
//go back
}
});

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.

why do i have to reload the app after change direction from LTR to RTL in react-native?

I use I18nManager.forceRTL(true) in initialState or componentDidMount life cycle but it does't work and if I reload my App, it works.
I mean I have to reload the App to see the effect, what is the reason?
Thanks in advance
Android:
Inside MainApplication:
import com.facebook.react.modules.i18nmanager.I18nUtil;
Add these lines to onCreate():
I18nUtil.getInstance().allowRTL(this, true);
I18nUtil.getInstance().forceRTL(this, true);
iOS:
Inside AppDelegate:
#import <React/RCTI18nUtil.h>
Add these lines to applicationDidFinishLaunchingWithOptions:
[[RCTI18nUtil sharedInstance] allowRTL:YES];
[[RCTI18nUtil sharedInstance] forceRTL:YES];
For reloading the App you can use react-native-restart
After installing the module, you can use it like here:
// Add these at the top of your component file
import RNRestart from 'react-native-restart';
import {I18nManager} from 'react-native';
// Then use it
...
I18nManager.forceRTL(true);
RNRestart.Restart();
If you want to use it inside of componentDidMount function of your component, You need to run this code exactly once, not more:
// Inside of your component class
componentDidMount() {
if(!I18nManager.isRTL) {
I18nManager.forceRTL(true);
RNRestart.Restart();
}
}
You can use RNRestart.Restart() after I18nManager.forceRTL(true). It will not close your app.

React native touch id not working

I want to create touch id local authentication in react native. I used
npm react-native-touch-id
import React, { Component } from 'react';
import {
Platform,
StyleSheet,
Text,
View,
TouchableHighlight
} from 'react-native';
var LocalAuth = require('react-native-touch-id')
var YourComponent = React.createClass({
_pressHandler() {
LocalAuth.authenticate({
reason: 'this is a secure area, please authenticate yourself',
falbackToPasscode: true, // fallback to passcode on cancel
suppressEnterPassword: true // disallow Enter Password fallback
})
.then(success => {
AlertIOS.alert('Authenticated Successfully')
})
.catch(error => {
AlertIOS.alert('Authentication Failed', error.message)
})
},
render() {
return (
<View>
...
<TouchableHighlight onPress={this._pressHandler}>
<Text>
Authenticate with Touch ID / Passcode
</Text>
</TouchableHighlight>
</View>
)
}
})
but it says nothing, i followed this link
https://github.com/ElekenAgency/react-native-touch-id-android
Came here because I've got the same question, but looking at your code I assume you've got lost in mixing libs.
Looking at the line:
var LocalAuth = require('react-native-touch-id')
You're importing LocalAuth which I believe is a part of react-native-local-auth library built on top of react-native-touch-id, while following a tutorial for 3-rd library which is react-native-touch-id-android.
According to their example in the repo, your import should look like this:
import Finger from 'react-native-touch-id-android'
My guess for the reason it's not crashing on you is because you've installed react-native-local-auth somewhere in the process befor trying out react-native-touch-id-android.
Better start all over - go to package.json and remove the above mentioned libraries, then run npm install and then follow the step-by-step guide in the repo you've posted.
I'd be glad if you come back afterwards and report on whether it worked out or not. Good luck.
use this code it worked for me !
import TouchID from 'react-native-touch-id';
TouchID.authenticate('Authentication')
.then(success => {
// Success code
})
.catch(error => {
// Failure code
});

Categories

Resources