React Native StackNavigator disappearing on re-entry - android

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.

Related

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.

Why do I have so many problems refreshing code in Android emulator

So I have just started learning app development with React Native this past week and so I've been getting into using Android studio emulators to run my apps. I've noticed that when I run the apps on the emulator, it doesn't seem to refresh the code properly. For example I made this very simple app while following a tutorial:
import React from 'react';
import { Text, View, Platform } from 'react-native';
import {Button} from 'native-base';
export default class App extends React.Component {
render() {
return (
<View style={styles.container}>
<Text>Testing 12321</Text>
<Button><text>Hello World!</text></Button>
</View>
)
}
}
const styles = {
container: {
flex: 1,
marginTop: 24
}
}
But when I first created it, in the tutorial the instructor forgot to put the Text tag within the button, so when I ran the app I got an error of:
Text strings must be rendered within a <Text> component.
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4137:14 in <anonymous>
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:4134:2 in createTextInstance
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:15909:12 in completeWork
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:19409:28 in completeUnitOfWork
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:19380:30 in performUnitOfWork
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:19347:39 in workLoopSync
- node_modules\react-native\Libraries\Renderer\implementations\ReactNativeRenderer-dev.js:18997:22 in renderRoot
* [native code]:null in renderRoot
and it keeps going. Then in the tutorial the instructor noticed the mistake and fixed it by adding the tag in the button, but then when he does that, the app refreshed properly and displayed the button but for me I still get this same error. I've noticed this happening quite often with the Emulator, it doesnt like to refresh properly when opening a new app or altering one. I have deleted and recreated the emulator like 5 or more times now to test out different apps. Does anyone have a suggestion of how to fix this? Thanks in advance!
Change your <text> to <Text> as below.
<View style={styles.container}>
<Text>Testing 12321</Text>
<Button><Text>Hello World!</Text></Button>
</View>
Feel free for doubts.

How to speed up initial launch app of React native and Expo app

I have created an app and added splach screen. It takes 1 second to load the app on Android Emulator. However after I publish the app in the store, it takes 4 seconds to load.
This is pretty annoying for such a simple app.
I was thinking it was because of _loadResourcesAsync func to load pictures. Therefore I commented out those lines but nothing has changed.
Any recommendation to speed up my app launch.
Here you can find my app.js
import React from 'react';
import { Platform, StatusBar, StyleSheet, View } from 'react-native';
import { AppLoading, Asset } from 'expo';
import AppNavigator from './navigation/AppNavigator';
export default class App extends React.Component {
constructor(props) {
super(props);
this.state = {
isLoadingComplete: false,
};
}
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}>
{Platform.OS === 'ios' && <StatusBar barStyle="default" />}
<AppNavigator />
</View>
);
}
}
_loadResourcesAsync = async () => {
return Promise.all([
Asset.loadAsync([
// require('./assets/images/big_bottle.png'),
// require('./assets/images/bottle.png'),
// require('./assets/images/coffee.png'),
// require('./assets/images/juice.png'),
// require('./assets/images/menu.png'),
// require('./assets/images/tea.png'),
// require('./assets/images/water-glass.png'),
]),
]);
};
_handleLoadingError = error => {
// In this case, you might want to report the error to your error
// reporting service, for example Sentry
console.warn(error);
};
_handleFinishLoading = () => {
this.setState({ isLoadingComplete: true });
};
}
const styles = StyleSheet.create({
container: {
flex: 1,
backgroundColor: '#fff',
},
});
Based on my experience, hybrid apps, in general, tend to launch slower.
Based on my tests, even a completely blank Expo app, based on the device performance, can take anywhere from 1-4s to launch. This is the time it takes from the moment the user taps (opens) the app to the time the splash screen gets hidden (and the first app screen is visible).
What you can do to speed it up is a really broad topic. Here are two recommendations, that helped a lot for a project of mine:
Cashe your assets.
Bundling assets into your binary will provide for the best user experience as your assets will be available immediately. Instead of having to make a network request to the CDN to fetch your published assets, your app will fetch them from the local disk resulting in a faster, more efficient loading experience.
Always load a cached version of your app first.
You can configure the "updates" section in your app.json to always starts with the cached version of your app first (fallbackToCacheTimeout: 0) and continue trying to fetch the update in the background (at which point it will be saved into the cache for the next app load).
Unfortunately, as of today, it looks like we're kind of limited to what we can do to improve further the initial loading time on a blank app. There isn't really a reliable way to know how long the 'check for updates' process takes, the general React Native initialization time, the 'load all the JS' time.
There is a feature request for tools for improving the startup time, would be great if the Expo team introduces something similar anytime in the future.

make browsers with react native

I have a mobile site and I want to make an android browser app where I want to open my site.
I have tried and react-native-browser. Something like..
import {
processColor, // make sure to add processColor to your imports if you want to use hex colors as shown below
} from 'react-native';
// at the top of your file near the other imports
var Browser = require('react-native-browser');
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
{Browser.open('https://google.com/')}
</View>
);
}
}
But got no success...
I just want to make a browser that opens my mobile site..
Is there any better way of doing this or if someone has any idea how to use react-native-browser ?
Thanks in advance
Looking at the source code, it seems this browser is only available on iOS.
you must search in this web https://js.coach/react-native?search=browser for example https://github.com/d-a-n/react-native-webbrowser
Why are you trying to look for an external library while there is a WebView Component already integrated with react-native itself?
WebView renders web content in a native view. You can use this
component to navigate back and forth in the web view's history and
configure various properties for the web content.
You can just add a WebView and open up the desired web url.
Example
import React, { Component } from 'react';
import { WebView } from 'react-native';
class MyWeb extends Component {
render() {
return (
<WebView
source={{uri: 'https://github.com/facebook/react-native'}}
style={{marginTop: 20}}
/>
);
}
}
You can use https://www.npmjs.com/package/react-native-webbrowser
Install:
npm i react-native-webbrowser --save
Use:
class SampleApp extends Component {
render() {
return (
<View style={{paddingTop:20, flex:1}}>
<Webbrowser
url="https://your-url.com"
hideHomeButton={true}
hideToolbar={true}
hideAddressBar={true}
hideStatusBar={true}
foregroundColor={'#efefef'}
backgroundColor={'#333'}
/>
</View>
);
}
Set all the hide's props to true to make your app display only the site

How to link a button to detail screen in React Native

What is the best practice to link the whole area below to another screen?
The whole area marked with orange needs to be clickable and takes to another screen. Should I put this whole area into a view? What is the best practice here? Would appreciate a code example if possible.
Yes, you can wrap that orange area using View component and try to use React Native Router Flux, the most popular and easiest way to navigate the screen/component.
import React, { Component } from 'react';
import { TouchableOpacity, View } from 'react-native';
import { Actions } from 'react-native-router-flux';
...
export default class HelloWorldList extends Component {
...
render() {
return (
<TouchableOpacity onPress={() => Actions.componentkey}>
<View>
...
</View>
</TouchableOpacity>
);
}
}
Moving from one scene (screen) to another is called navigation, there are plenty of (good and bad) navigation components packages/modules out there and most of them have an Example app to test and learn.
I'd recommend React Navigation as it's really well documented and very easy to understand also it's covered in React Native docs as well.
Other packages:
react-native-router-flux
react-native-navigation
native-navigation

Categories

Resources