Sign in
Log inSign up
What we learned after using React Native for a year

What we learned after using React Native for a year

Sriraman's photo
Sriraman
·Nov 11, 2016

At Witworks we have been developing the Blink smartwatch, which runs on a customised version of Android called Marvin. We have also been building companion Android and iOS apps, for the smartwatch, in React Native (RN). If you're evaluating React Native for your next project, or just curious about it - here is my story. In this article, I will share our overall experience with RN, and the things we have learned so far.

If you want to know more about the Blink watch, you can check out articles about us on TheNextWeb, Mashable and Tech in Asia.

Background

Right about a year back, we have started the development for the Blink smartwatch, and I had taken the responsibility of making the companion mobile apps. Apart from just syncing the data between the watch and server, our Blink mobile apps have lots of features; such as the app store, music player, timeline, watch face store, and more.

Like many startups, we had the debate on how to go about building our mobile apps and faced the typical set of trade-offs. We just had a JavaScript team at the time. On the other hand, we wanted native-like performance and native-like feel for our apps. As Ionic framework was trending at that time, we did many experiments with it, but unfortunately the performance was not on par with what native solutions had to offer.

Our team was on the verge with the decision to develop native apps for both platforms, but luckily we got to know about React Native which promised native look, feel and speed. We gave it a try, and to be honest we couldn't feel any difference between the native app and the React Native app built by our team.

I should also mention that our app involves a lot of background tasks and communication between our watch and the phone. Unfortunately, these things can't be done with Javascript alone. So, we started exploring the bridges provided by React Native. After building a few simple apps and running a few experiments, we got to know that all types of communications can be achieved between the native code and React Native (at least for all our use cases).

After the initial research, we started developing all the features in JavaScript, except the background tasks.

P.S. We are using Swift for iOS, and Java for Android; for the development of background tasks.

Let me highlight a few things that we have come to like the most about React Native.

Hot reloading & Live reloading

We love the live reloading capabilities in React Native. In native languages, we have to wait until the completion of compilation, to see the final output. But React Native lets us modify the app without recompiling it. JavaScript code is loaded from the local server during development, and is packaged into the app with other resources when we release the app; which definitely speeded-up our UI iterations.

React Native also has a feature called Hot Reloading. It keeps the app running, and injects the new changes in realtime. At this time, hot reloading is not working perfectly in a few situations, but a reload of the whole app, usually fixes it.

Debugging Tools

Inspector

Just like we can debug the elements on the web, we can also debug the elements in React Native. There is an inbuilt inspector inside the dev menu, which is very useful for UI debugging.

Chrome Debugging

Currently, Chrome debugging can be used only for accessing the console. As of now, the 'React' tab does not work with React Native. But, there are on going discussions to make it work. Meanwhile, you can use Nuclide's "React Native Inspector" as a workaround.

Read more about it here.

Reusability

Another amazing feature of React Native is that the same generic code can be used on both the iOS, and Android platforms. For instance, if you use React Native's switch component, it will render UISwitch in iOS devices and the Native Switch component in Android. In our Blink app, we have reused around 80% of the JavaScript code between Android and iOS.

Stylesheet with Flexbox

Layouts in React Native are implemented with CSS-like stylesheets which allow you to customize your components' properties such as border, height, width, color etc. The amazing thing about it is that a component can specify the layout of the children using flexbox, and we don't have to care about the browser compatibility.

Flexbox works the same way in React Native as it does in CSS on the web, with a few exceptions. The defaults are different, with flexDirection defaulting to column instead of row, and alignItems defaulting to stretch instead of flex-start, and the flex parameter only supports a single number.

Easy to expose Native API

We have implemented custom data transactors in Java, and Swift for the communication between our watch and the mobile phone and have exposed the APIs for these data transactors to React Native. It has also been very easy to expose the Native modules and Native UI components to RN.

P.S. I have also written a small story on Hashnode about how I built a Custom Android Module for React Native.

Immediate deployment

When we push an update to Android/iOS, it goes through the corresponding approval processes. In Android, it usually takes around 24 hours to reach users' phones. In iOS, it takes around 3/4 days. As a result, if we want to push an update after fixing a bug, we usually have to wait for 3 to 4 days to send a fix to the users.

However, things are different with React Native. Using Codepush or AppHub, you can easily push instantaneous updates to the users' devices. Your users don't have to do a thing to get the updated version of the app. When we were testing our app with our beta testers, we used to get lots of bug reports. Codepush helped us send updates in a timely manner.

Community

React Native's community is one of the best open source communities. There is also a public Facebook group called React Native Community which helped me a lot when I faced a few certain, weird bugs while developing the app.

Problems Faced

Problems Faced

There are of course many more features in React Native that we love, but the ones above are the most important ones. Now, let me quickly highlight the top 3 pain points which we faced.

Animation API

Animation APIs run all the processes on the JS Thread. Consequently, we had faced some performance issues while using these APIs. This is one of the most upvoted issues on Product Pains : Offload some animations from JS thread for better perf. React Native community is slowly moving many animations to native, and the performance is much better than before.

Frequent Release cycles

React Native has a two-week release cycle. There are many features, and bug fixes in each release. But every release, often brings breaking changes as well (maybe in a “move fast and break things” spirit specific to Facebook). In these cases, we have to spend a lot of time fixing things while upgrading the code.

Lack of Resources

When we started a year back, there weren't enough resources to know about many of the hidden features of RN. Even the documentation was not proper. Getting the answers to React Native questions was very difficult. But now the community has grown much bigger, and the documentation is getting better every day.

How we tackled the performance issues

React Native Performance Issues

While building the companion apps, we came across some performance issues. Eventually we managed to fix all of these issues, but the following ones are worth mentioning :

ListView

We had a use case where we had to show all the songs from a phone in a ListView. A few of the phones we tested had more than 3000 songs. It was taking a lot of time to load all of the cells while using the ListView. So, we tried to load the songs whenever users reached the end, or 20 cells before the end. But, we faced two major hurdles :

  • JavaScript couldn't render it quickly when someone scrolled too fast.
  • It took lots of memory if users scrolled through all the 3000 songs.

Soon after that we started experimenting with SGListView, which is a memory minded implementation of React Native's ListView. It reduced the memory footprint, but in our case the rendering was nowhere close to native apps. We decided that longer lists couldn't be done in React Native and made this particular part in Swift and Android. In the meantime, RN team was making a lot of changes to improve the performance (Now it has improved a lot, but is still not comparable to native performance, IMO). Nevertheless, I was keeping an eye on all the ListView related implementations by React Native community back then, and I came across two interesting implementations :

Tal Kol's Implementation of UITableView

Tal Kol used the UITableView to render the JavaScript view inside every cell. There is a problem with this approach too. Native is fast, and Javascript is also relatively fast. But, the bridge couldn't match this speed. So, this was not working properly, at least for our use case.

Aksonov's Implementation of UITableView

Actually, Asksonov built two kinds of Lists in the Repo. One is exactly like Tal Kol's implementation. In another one, instead of using the bridge heavily, he made the styling in UITableView itself and exposed a minimal API to customise the design. This offered amazing performance without having to write a single line of Swift or Objective-C code. However, if you want to make major modifications to the UI, then you would have to write Swift or Objective-C code.

Eventually, we made our own implementation of ListView for our use case which is based on Aksonov's Implementation.

Slow page transition

When we push a route to the navigator, JavaScript needs to render all the components which are necessary for the new scene, and it also needs to do the page transition in the same JS thread. Doing both the things simultaneously was giving a choppy transition to us. So, we started scheduling the animation using the InteractionManager. While making the transition, we load lesser content and render the remaining content, once the transition is complete. It helped us provide a smoother experience to our users.

JS Thread stalls on animating the image size

We noticed a frame drop while trying to animate the image size. Soon we got to know that the image was re-cropped and scaled from the original image when we were trying to adjust the size of the image. Transforming the image using scale property fixed the issue for us.

Out of Memory Exceptions

iOS is a lot more forgiving than Android, with memory usage. Initially, our application was suffering a lot of memory issues, which often lead to OutOfMemoryException in Android. Eventually, we experimented a bit, and reduced the size of the images as much as possible, to get rid of this error. React Native Android is based on Fresco for loading and displaying images. Downsampling is disabled for PNGs in React Native, since it is still experimental. So, we have started using JPG images whenever possible.

Conclusion

Based on our experience with React Native, we can confidently say that React Native provides the same benefits as every other JavaScript based mobile framework with a native feel and performance. In our case, I could implement everything in JavaScript and could even edit minor things in Java and Swift code of the existing modules, without any prior experience in either of them. This is a huge plus point.

Lastly, I have got the following 5 tips for anyone who is evaluating React Native:

  • Start by implementing everything in JS for maximum productivity.

  • If traditional React optimizations fail, surgically move the troublesome parts to the native part for better performance. But, don't overuse the bridge.

  • For animations/interactions, try to use declarative libraries like Animated whenever possible. However, some complex interactions can't be expressed declaratively, and offloaded.

  • Use JPEG images whenever possible.

  • Investing time on learning more about the React Native internals will be very helpful in making better apps.