This article will deal with the first challenge we had to face: the navigation. But Before diving into the technical details, you might be wondering why you would switch to React Native.
Here are the main advantages:
One of the key selling points is that you develop one single code for both iOS and Android.
When editing your code, reloading the app to see your changes takes only a few seconds. No need to wait for the Gradle build to finish anymore!
Plus, you can benefit from features similar to those available to web developers, like hot reloading:
React Native consists of Javascript code controlling Native UI.
An ++code><Image />++/code> React component will display a native ++code>UIImage++/code> on iOS or a native ++code>ImageView++/code> on Android.
So React Native is essentially Native! If you open your React Native app in the XCode hierarchy inspector or the Android Studio one, you'll indeed see the Native UI components:
That's why React Native is an adequate solution to keep performance and UX consistent with the native app.
The first challenge that we need to tackle is how to handle navigation.
When using React Native, the officially recommended solution for navigating between screens is react-navigation. But it's a full javascript solution so that's not a good fit here.
Let me explain, we need to keep a consistent navigation stack, right?
For instance, you might want to have a hybrid navigation stack like so:
We therefore need a solution aware of the whole navigation stack.
Plus, we also want to keep a consistent look and feel throughout all the app. The user should not know whether he is seeing React Native components or pure native components.
Lucky us! Airbnb is developing a great Open source solution with that exact goal in mind: integrating React Native screens inside their apps. This solution is Native Navigation.
Since they're not actively maintaining it, we forked it to @bam.tech/native-navigation and added a few features (such as XCode 9 support and iPhone X support).
To install it, checkout the installation guide.
Once installing ++code>native-navigation++/code> is done, it is fairly simple to manage the navigation stack:
++pre>import Navigator from "native-navigation";
import ArticleDetailScreen from "./screens/ArticleDetail";
Navigator.registerScreen("ArticleDetail", () => ArticleDetailScreen);++/pre>
++pre>import NativeNavigation
...
let screen = ReactViewController(
moduleName: "ArticleDetail",
props: ["articleId": articleRef.identifier as AnyObject],
)
navigationController?.pushReactViewController(screen,
animated: true)++/pre>
in Java for Android
++pre>final Bundle props = new Bundle();
props.putString("articleId", article.getId());
screenCoordinator.pushScreen("ArticleDetail", props);++/pre>
++pre>Navigator.push("ArticleDetail", { articleId: article.id });++/pre>
For instance on iOS:
++pre>import NativeNavigation
@objc(NativeRedirector)
class NativeRedirector: NSObject {
@objc(openNativePage:)
func openTeamPage(options: NSDictionary) -> Void {
let viewController = NativeViewController.instantiate()
if let navController =
ReactNavigationCoordinator.sharedInstance.topNavigationController() {
// Native React Native bridges are executed in a separate thread
// You need to dispatch in the main thread to push a view controller
DispatchQueue.main.async(execute: {() -> Void in
navController.pushViewController(viewController, animated: true)
})
}
}
}++/pre>
To ensure a consistent navigation between purely native and React Native, we need to make sure the navigation bar and its transitions in React Native match what the native side does.
Fortunately, the navigation bar with ++code>native-navigation++/code> is actually native! It should be pretty familiar to native developers:
The other good news is you can customize it for your screens, from the Javascript side with ++code>Navigator.Config++/code>. It's the React Native way, it's a Javascript component controlling Native UI.
You can checkout here the list of options available.
++pre>import Navigator from "native-navigation";
import ArticleDetailScreen from "./screens/ArticleDetail";
Navigator.registerScreen("ArticleDetail", () => (
<Navigator.Config
translucent
statusBarTranslucent
elevation={5}
rightButtons={[
{
title: "Right button"
}
]}
onRightPress={() => alert("button clicked")}
>
<ArticleDetailScreen />
</Navigator.Config>
));++/pre>
Here's an example of the Kickstarter iOS app hijacked by React Native :
With its React Native code:
++pre>import React from "react";
import { Button, NativeModules, Text, View } from "react-native";
import Navigator from "@bam.tech/native-navigation";
Navigator.registerScreen("RNScreen2", () => () => (
<Navigator.Config title="RN Again" backButtonTitle="">
<View
style={{
flex: 1,
justifyContent: "space-around",
alignItems: "center",
paddingVertical: 100
}}
>
<Button
title="Open native screen"
style={{ fontSize: 30 }}
onPress={() => NativeModules.NativeRedirector.openTeamPage({})}
/>
</View>
</Navigator.Config>
));
Navigator.registerScreen("RNScreen", () => () => (
<Navigator.Config title="React Native!" backButtonTitle="">
<View
style={{
flex: 1,
justifyContent: "space-around",
alignItems: "center",
paddingVertical: 100
}}
>
<Text style={{ fontWeight: "bold", fontSize: 20 }}>BRO</Text>
<Text style={{ fontSize: 20 }}>Do you even React?</Text>
<Button
title="Open RN Screen"
onPress={() => Navigator.push("RNScreen2")}
/>
</View>
</Navigator.Config>
));++/pre>
Do you have any other issue in mind about integrating React into existing native apps? Feel free to tell us what you want to hear about next in the comments! :)