Experience Learning React Native as a Web Developer
My journey to creating a mobile app with React Native as a web developer.
August 30, 2024
With the creation of Music, I finally took the plunge into learning React Native, something that I was meaning to do for around a year but never really had an opportunity or reason to do so. In short, it’s a good thing to learn if you want to explore the mobile scene by creating native apps that can also work on the web (given the APIs & libraries that you use are compatible).
What is React Native
React Native enables developers to use their knowledge of writing React code to create universal, native apps for Android, iOS, Web, and more. You don’t need to learn Java, Kotlin, Objective-C, or Swift to build mobile apps — you can use JavaScript (preferably TypeScript) instead. In addition, your React Native code can build both Android & iOS apps, removing the need for separate codebases for different platforms.
Differences Compared to the Web/React
Although we can reuse most of what was learned in React (such as hooks) to create React Native apps, there are some differences.
No DOM
React Native doesn’t have a DOM, meaning you can’t use regular HTML elements in your code (ie: <div />
, <p />
). Instead, React Native exports native components (ie: <View />
, <Text />
) which maps to each platform’s UI building blocks that you can use to structure your app. There are additional differences to how you structure your code — for example, text must be wrapped in a <Text />
component:
/* What we do on the web. */
<div>Hello world!</div>
/* What we do on mobile. */
<View>
<Text>Hello world!</Text>
</View>
Though as of recently, Evan Bacon, creator of the React Native framework, Expo, teased a new "use dom"
directive that would allow your web code to work in React Native (ie: translate over the old HTML structure to work with React Native).
Styling
React Native uses Yoga as its styling engine. There’s no global CSS file where you can define reusable classes. The only way you can style components (without any third-party libraries) is with the style
prop available on components. In addition, there’s the caveat that not all of the CSS properties are supported (most notably, CSS Grid). Even if a CSS property is supported, it might have some behavioral differences.
The primary way of positioning components in React Native is with Flexbox, with all elements having the following defaults: display: flex
, flexDirection: column
, alignContent: flex-start
, and flexShrink: 0
. This is different from the web where for example, flex-direction
defaults to row
.
Although React Native only supports the style
prop, you can add support for styling libraries by adding third-party libraries such as NativeWind for Tailwind CSS support. This provides the familiar and simple styling interface used on the web.
Missing APIs
While coding with React Native, I found out the hard way that some APIs that I assume that were part of JavaScript weren’t and were only available on the web or with a DOM. However, Meta has been working on porting some of the most used APIs with Hermes. One of the things that they recently added support for was atob()
& btoa()
with React Native 0.74.1, however, some things are still missing such as TextDecoder
.
Rendering Lists
In React, when you want to render a list of items, you would typically use Array.map()
. However, on mobile, that’s a bad idea since all of those items will be rendered immediately, causing performance penalties. The correct approach is to use <FlatList />
, however, it has some performance and memory usage problems as well. The better approach is to use Shopify’s <FlashList />
if you can overcome its styling limitations.
Minor Logic Differences
There’s some things you need to keep in mind when rendering components. In React, you typically could do the following to conditionally render elements using the logical AND (&&
) operator:
{ 0 && <MyComponent /> }
{ "" && <MyComponent /> }
{ NaN && <MyComponent /> }
However, these will all cause the app to crash. The reason is something we mentioned earlier — text needs to be placed inside of a <Text />
.
The Learning Experience
From the resources I found, Expo seemed the best way to create a new React Native application. Expo provides features all mobile apps need such as routing/navigation, along with a vast assortment of universal libraries we can use right out of the box, with good documentation.
Regular React Libraries
I could really see the idea of React skills translating to React Native through the libraries used on the web being supported such as Drizzle ORM and Jotai. I’ve also finally taken the time to learn React Query due to the good DX (Developer Experience) when doing asynchronous tasks such as amount of data fetching and that too, works in React Native.
Expo Go & Development Builds
Early on in the project, since I went with Expo as my method of writing React Native code, I’ve been using their libraries (ie: expo-*
) to create my app as they required no setup and allows for fast development as they work with Expo Go, a sandbox for testing our app by scanning a QR code.
However, it can only go so far as some libraries that I needed required a development build as they use native code. A development build requires a bit more setup (ie: needing Android Studio installed) and builds the app on our computer which is then sent to our device over USB as an APK. Although there’s some extra steps required, the advantage of using a development build is that we have access to more libraries and therefore more options with creating the app.
Creating NPM Packages
Speaking of libraries, during this process, I also ended up learning how to create NPM packages due to wanting to learn more about audio metadata and how we could extract them.
My first attempt was extraction via JavaScript through going through the bytes of the file and extracting the metadata from that, which is similar to the jsmediatags library, however there were unfixed issues that led to it being unusable on React Native. As I felt this logic could be helpful to others if they wanted a metadata extraction library, I researched how I could create a React Native library along with the process of releasing an NPM package; which was a bit more difficult than I expected. However with the help of callstack’s create-react-native-library
tool, the process was more manageable.
After using my package with some testers, they found it to be a bit slower than to their liking, so I ended up searching for a better solution. I landed on utilizing Android’s native APIs to get the metadata for me through the MetadataRetriever API. However, the problem is that you can’t directly access that code with the method we’ve used to create the previous package. This is where I learned how to create Native Modules, which is code written in the native mobile languages (in my case, Kotlin) that can be interfaced through JavaScript. Again, I didn’t really know what to do, but the create-react-native-library
tool really helped get me started.
Although typically, you probably wouldn’t need to learn how to release an NPM package & create a Native Module, this might be helpful to those who can’t find an existing solution to their problem.
Page Routing
Expo utilizes file-based routing to create screens we can navigate to. A router is a must-have in mobile applications as otherwise, all your content basically has to be in a single screen, which is probably not optimal from an experience perspective. File-based routing is something I’m used to as I’ve worked with Next.js’ App Router prior to this.
Some things you do need to keep in mind is how you want to lay out your routes with respect to the _layout.tsx
file, which contains UI that’s shared across screens. With mobile apps, the layouts can be pretty crazy and you need some good organization to prevent routes from accidentally having unnecessary UI elements. I’ve faced this problem with having certain screens contain a mini track player, however in some places, there would be different shared UI, which resulted in a lot of route groups to preserve the shared mini track player, but have the remaining shared UI be separate.
Conclusion
The rest of the process was, well, basic React — we create reusable components, combine them together to create screens, and come up with terrible naming schemes & organization. Maybe not the last part — the last part is mainly on me for experimenting with figuring out what the best project organization structure is for me. In the end, React Native is pretty similar to React and if you want an easy entry point into mobile development, React Native is a good option.