Migrating to React Native

A one way journey

Juan M Rodriguez
6 min readFeb 6, 2017

Building a new project with React Native and being able to rapidly iterate on it is a lot of fun. What’s not as fun: not being able to use RN in an already existing iOS / Android native application. Having to work in the old way — duplicating business logic, testing, framework, etc. — can really bring you back down to earth.

There’s no doubt that React Native is the way to go for new projects. However, that may not be the case for migrating an existing one. There’re many things to take into account and challenges on how to integrate the old and the new world.

In this article I’ll present some of the biggest challenges and solutions to those braves out there, prepared to start migrating those old native apps into RN.

I’ll include some code samples for the more interesting parts as a reference. The code will be for iOS only given that I don’t want to make the article too long, and there’s a similar solution for Android if you get the idea for iOS.

Should we move to React Native?

It may not be you, but many people still doubt that RN is the right tool for serious production environments, especially in the enterprise world, where applications will live for many years and you have to think on the long run, where choosing the wrong tool could cost a lot.

I won’t spend a lot of time explaining why it’s worth the effort to move to RN given that are a lot of resources about the topic, but I’ll mention the two most important reasons:

  1. One source of truth: Having to support two platforms forces code duplication, which is a huge waste of time and money. But not only that, you have to keep two teams working in different languages, using different frameworks and tools. Even hiring for that is really challenging.
  2. Development speed: Given that there’s no need to compile your app every time you make a change, development with RN is much faster. You can make use of many mature javascript libraries out there, as long as they don’t use the DOM.
  3. Collaboration: Front-end developers, designers and prototypers are used to work with JS, so RN environment is much more familiar to them, even giving them the opportunity to try making changes by themselves.

Still it’s true that there are some flaws like bugs in the code base, breaking changes in new releases and some missing features. I’ve also heard people having performance problems, but those in general are due to lack of experience with the platform. In any case, RN is moving forward really fast, tackling all problems mentioned before while adding new and cool features like support for Windows Phone, Apple TV and more.

And if you’re worried about something you’re currently using on native and you want to keep using it, don’t worry, it’s possible and simple. I’ll talk about this later on.

How does React Native integrate with Native?

Native applications are basically a composition of views. In order to use react native into a native application you have to follow the same pattern, by using RCTRootView for iOS andReactRootView for Android. We also need to indicate to these views which RN application to use, those we declare using AppRegistry.registerComponent

If you’re not familiar integrating RN with an existing app I’d recommend you to follow the steps here.

We also need a container for the RN view to run. In native, views are contained by a UIViewController in the case of iOS and an Activity in the case of Android. The RN view is just another native view so it will follow the same structure, but we have to define what is going to be the relation between the container and the RN views.

  1. One container, multiple RN views: As any kind of views, RN views can be added to any part of the native view hierarchy and can be combined with other types of views. Even when possible it’s not recommended given that we may run into layout and communication problems between the views.
  2. One container, one RN view: The container will have only one RN view covering the whole viewport. This is the best option because it keeps the structure simple and it allows you to handle the view hierarchy inside RN.

In any either case, remember to create only one bridge and share it across all of your RN views. You can use the following UIViewController container as a reference for iOS:

How to consume my current native classes?

Sooner or later you’ll need to consume a native component that is not available in react native. For that you’ll have to expose your native component by implementing a few classes and methods.

Everything on RN works based on native UI components and modules. Both are very similar, the only difference is that native modules don’t have a native view associated with them.

UI components and modules act as the native counterpart of javascript components. Think about the standard View component in RN. In iOS it’s represented by RCTView (UIView subclass) that is managed by RCTViewManager. Similar on Android, it’s represented by ReactViewGroup (ViewGroup subclass) that is managed by ReactViewManager. This same logic applies to any RN component.

If you aren’t familiar with native modules/components I’d recommend you to read Native Modules for iOS and Android and Native UI Components for iOS and Android

Next, I’ll present some common UI components and modules that you may have to implement:

  • State: As you start using RN, all your state is stored natively and you might have no have plans to move it to RN because of other native dependencies. So, you need a way to expose it into RN and for that I’ll use an approach similar to redux.
  • Action: If you want to mutate your state from RN, you’ll have to expose the corresponding methods in a native module. Again, same concept exists in redux.
  • Navigation: You may have a button in your RN view that when pressed navigates the user to a native component. For instance, having master and detail containers where master is RN and the detail pure native. You need a way to push/invoke the detail from master.

If the state you need to consume in RN doesn’t change, you can simple send the state as a parameter at the moment of creating the RN root view. The state will by present in RN under props.

The component native model will look like this:

  • In green, the RN bridge running our javascript.
  • In yellow, the native UI components and modules.
  • In blue containing everything, the native environment.

State

To deal with the state of the native application will need a way to subscribe to changes made the state. For that, we’ll create a native module based on RCTEventEmitter:

This module will provide the nativeStateDidChange event to RN and a getState method to read the state. You could pass the state as part of the event but it’ll cause problems if you have several events altering the state, having to send the whole state from each of them.

Action

Now that we can consume the native state, we need a way to mutate it. In order to do that we’ll follow the same pattern as for state. We’ll create a native class and expose all the actions that our the business model requires to be triggered from RN. One minor change is that the native class will inherit from NSObject <RCTBridgeModule> instead of RCTEventEmitter because we don’t need to subscribe to any event: once the action is sent, it’ll change the native state, which will trigger an event through RNNativeState. This is the same pattern that redux follows.

Navigation

There are different approaches on how to navigate to a native module. You can choose to present the native module independent of the context, or if you already have some routing logic implemented natively, you can just call it. Depending on your decision, you may opt to implement it using a module or UI component.

For our sample, I’ll assume that the RNViewController that contains our RN application:

  • Will be pushed on top of a UINavigationViewController.
  • Will push a nativeUIViewController by sending a route.

As you can see, we have to implement a UI component by subclassing RCTViewManager because we need a reference to the native UIView to be able to access the underlying UINavigationViewController.

Conclusions

Migrating your current native application to react native is possible and it can be done gradually.

You’re able to use any native class by implementing a native UI component or module. Use UI components only if you need to access the native view or its hierarchy.

--

--