I'm trying to create an Android version of my React Native app but I'm having problems getting my head around the Android navigator. The code I use in my index.ios.js is:
render() {
return (
<React.NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Product Searcher',
component: ProductPage,
}}/>
);}
How do I create an equivalent Navigator for Android in my index.android.js? I've looked at this piece of documentation (https://facebook.github.io/react-native/docs/navigator.html) but I can't seem to find a way to produce equivalent navigator logic using this tutorial even though my iOS navigator is very simple.
A very simple use of the Navigator would be something like:
class MyApp extends Component {
render() {
return (
<Navigator
initialRoute={name: 'productpage'}
renderScene={this.renderScene}
/>)
}
renderScene(route, navigator) {
switch (route.name) {
case 'productpage':
return (<ProductPage navigator={navigator}/>)
}};
In renderScene you will define all your scenes for the app. By passing the navigator as a property to the different scenes you can access the navigator.
Related
I am implementing Microsoft's AppCenter for Crashalytics and Analytics. In Native development, I am able to add two lines of code and it adds pageview analytics (track each time a page is viewed) to every page in the app. I am curious to know if there's an easy way I can do it in a React Native app as well. This is what it looks like on native:
In native iOS, I just create a BaseViewController and make sure every ViewController inherits from that class. And then in the ViewDidLoad of the BaseViewController I add something like the line below.
And in native Android, I just create a BaseFragment and make sure every Fragment inherits from that class. And then in the OnCreate of the BaseFragment I add something like the line below:
Analytics.TrackEvent("PageView: " + this.GetType().Name)
How can I implement something similar on React Native? I am guessing that since I am using a across all pages, I could somehow create a BaseSafeAreaView that I can then add similar analytics code in the UseEffect?
How about Mixpanel ? You can implement a fully blown analytic solution.
They have a react native integration
segment is also great. They provide support for [react native integration] as well. You can integrate segment with mixpanel if necessary.
Firebase analytics is also a good solution. This can be done with rnfirebase analytics package
Since I am using react-navigation, I was able to use this with my NavigationContainer so I didn't need any base class
...
export default () => {
const navigationRef = useNavigationContainerRef();
const routeNameRef = useRef();
return (
<NavigationContainer
ref={navigationRef}
onReady={() => {
routeNameRef.current = navigationRef.getCurrentRoute().name;
}}
onStateChange={async () => {
const previousRouteName = routeNameRef.current;
const currentRouteName = navigationRef.getCurrentRoute().name;
if (previousRouteName !== currentRouteName) {
await Analytics.trackEvent('PageView: ' + currentRouteName);
}
routeNameRef.current = currentRouteName;
}}
>
...
When I am using Navigator from react native I am getting an error. The error is:
Warning: React.createElement: type is invalid -- expected a string (for built-in components) or a class/function (for composite components) but got: undefined. You likely forgot to export your component from the file it's defined in, or you might have mixed up default and named imports.
Check your code at App.js:11.
My code is:
import React, { Component } from 'react';
import {
Text,
Navigator,
TouchableHighlight
} from 'react-native';
export default class App extends Component {
render() {
return (
<Navigator
initialRoute={{ title: 'Awesome Scene', index: 0 }}
renderScene={(route, navigator) => (
<Text>Hello {route.title}!</Text>
)}
style={{ padding: 100 }}
/>
);
}
}
I followed this tutorial: https://reactnative.dev/docs/0.43/navigator
Can someone please help me in this. Is there something wrong in documentation?
This code example is from react doc version 0.43. After that "Navigator" was removed.
If you do not care about backward compatibility then I would suggest following current documentation (0.63). In the latest version, there is more simple solution is given.
I have a fairly straight forward set up with react-navigation that utilizes a switch navigator which accepts a param signedIn to set the initial route which looks like this:
const uriPrefix =
Platform.OS === 'android'
? 'myApp://myApp/'
: 'myApp://'
const createRootNavigator = (signedIn = false) => (
createSwitchNavigator({
LoginNav,
AllNav: { screen: AllNav, uriPrefix, path: '' }
}, {
initialRouteName: signedIn ? 'AllNav' : 'LoginNav',
})
);
I then call this in RootContainer:
const Nav = Navigation(isLoggedIn);
return (
<Nav />
)
On iOS this works fine with the uriPrefix and I am able to link to the screens using Linking.openURL() with the link.
On Android it does not work. I am able to make it work on Android without using the switchNavigator and just exporting LoginNav and AllNav, but I still need a switchNavigator.
I followed the instructions here: https://reactnavigation.org/docs/en/deep-linking.html, but am unable to pass uriPrefix in the way they define. I found that at least for iOS, I was able to define that inside of my switchNavigator, but it just does not work the same for me on Android.
Looks like it's a bug in react-navigation:
https://github.com/react-navigation/react-navigation/issues/5027
https://github.com/react-navigation/react-navigation/issues/4913
There's some workaround there that might help, I ended up changing all my navigation into stack navigation to support deep linking =(
I have a really strange behavior in my react-native application. I tracked it down to a minimal example.
I have a SwitchNavigator with two Components. The first Component does nothing except navigating to the second component after 2000ms. The second Component has an error inside the render method (for example it tries to render some undefined stuff "test.error").
Code Component 2:
render = () => {
return (
<View>
{
test.error
}
</View>
)
}
During development I get a normal "redscreen of death" which tells me "ReferenceError: ReferenceError: test is not defined". This is expected behavior.
But in release build, the application fails silently and just displays a blank screen. No crash of application.
More Information:
The same application does crash in release build (as expected), when the error part is displayed after the component did mount.
Component 2 - crashes as expected:
class Test extends Component {
state = {
error: false
};
componentDidMount = () => {
setTimeout(() => {
this.setState({error: true});
}, 0)
};
render = () => {
if (this.state.error) {
return (
<View>
{
test.error
}
</View>
)
} else {
return (
<View>
Test
</View>
)
}
}
}
Additional information:
when I use Component 2 as the initially called Component, my application does crash as expected. It seems only not to crash after navigate.
The behavior is the same with StackNavigator
Versions:
React: 16.3.1
ReactNative: 55.4
ReactNavigation: tried with 1.6.0 and 2.18.2
Bugsnag: 2.12.4
For anybody having the same issue. It had nothing to do with bugsnag or react-navigation.
The problem was, that errors inside the render method do not hard crash the application in release mode. I think the reason lies in React 16 and the ErrorBoundary feature.
The resolution in my case was to add a ErrorBoundary in my App.js (see https://reactjs.org/docs/error-boundaries.html). Here I'm now able to handle the error and report it to bugsnag.
componentDidCatch = (e, message) => {
bugsnag.notify(e);
Alert.alert(
I18n.t('GLOBAL__FATAL_ERROR'),
I18n.t('GLOBAL__FATAL_ERROR_MESSAGE'),
[
{
text: I18n.t('GLOBAL__FATAL_ERROR_RESTART'),
onPress: () => BackHandler.exitApp()
}
]
);
};
I have a React Native app working using NavigatorIOS API, and am trying to translate the same code to use Navigator API. My trouble is when creating a NavigatorIOS component, the navigator is implicitly passed, whereas in Navigator, you must call renderScene and make your own navigator prop. I'm not sure what navigator to pass (do i create one or what?).
The structure of the app is that there are 2 tabs: Book and Search, and BookList is another component which lists all the book entries, so that when you press one of them, it brings you to BookDetail. Right now, pressing a book brings me to the same page (BookList). I think this is because there's only one page in the route, but I'm not sure how to initialize the route and navigator.
This is where I call the navigator to push a scene onto the route in BookList:
showBookDetail(book) {
this.props.navigator.push({
title: book.volumeInfo.title,
component: BookDetail,
passProps: {book}
});
}
This is the NavigatorIOS version:
class Books extends React.Component {
render() {
return (
<NavigatorIOS
style={styles.container}
initialRoute={{
title: 'Featured Books',
component: BookList
}}/>
);
}
This is my Navigator version that is not working:
class Books extends React.Component {
render() {
return (
<Navigator
style={styles.container}
initialRoute={{ title: 'Featured Books', index: 0}}
renderScene={(route, navigator) =>
<BookList title={route.title} navigator={navigator}/>
}
/>
);
}
I see the body of your renderScene function returns the <BookList> component, so it's not really surprising that when it is called you just see another BookList. Try something like this instead:
renderScene={(route, navigator) =>
<route.component title={route.title} navigator={navigator}/>
}
As long as you have imported or required BookDetail in the source file for Books, renderScene will be passed the route object you specified in navigator.push, which has the component property BookDetail
Note that you will also have to change your initialRoute to have a component property set to BookList if you take this approach