Skip to main content

I'm using custom components for react-native, and you should too

· 9 min read

Alternate title: Ben's rant on NativeBase breaking everything.

I made Billy a few years ago, and I stopped updating it because I had trouble finding product market fit - until now. Apparently, petty cash management is an unsolved thing, unless if you're willing to shell out 100 USD for 10 employees. Even then, the functionality is buried inside a maze of ERP modules that your CFO has bought.

So I tried upgrading the React Native & Expo version for Billy, because despite its shitty UI, it gets the simple job of petty cash management done.

And then I broke everything. Specifically: NativeBase, the component library that I was using, broke everything.

When I first created Billy, I decided to use a component library that has everything ready to use out-of-the-box. After all, I just wanted to ship something without thinking about how nice it actually looks.

Naturally, I grabbed the one that had the most number of stars: NativeBase.

Back then, NativeBase wasn't particularly performant, though I didn't mind it because I was fairly confident that I could get away with subpar performance as long as I was smart about memoisations and virtualisations.

True to my prediction: throughout my time of actively developing Billy, NativeBase had never given me much trouble. Sure, there were a few performance quirks here and there (including it being super slow when you need to render a list), but they were all solvable with a little bit of "hey, there's no need for you to render that".

Unfortunately, after 3 months of developing Billy and showing it around as a home expense management app (big mistake), I couldn't find product market fit. People weren't interested with trying Billy out, as they preferred using Google Sheets and/or other existing expense management apps; I was running out of unpaid leave as well.

Fortunately, I got the perfect customer: myself.

To this day, I still use Billy to manage my family's petty cash. To non-Indonesians, there's a lot of things in Indonesia that you still need cash for: things like groceries from your local wet market, paying for parking, and even paying for your tennis court. Plus, issuing a dedicated credit card for my family seems, I don't know, overkill?

So I still need Billy to be up and running, because there just isn't any good solution for us out there (other than making a Google Sheets, but that's cumbersome and hard to operate).

And then Play Store demanded me to update my app

I was hoping that Play Store and App Store would leave Billy alone, because it didn't really need any updates for what I'm using it for. I don't need moderation, because it's just for my family members. I certainly don't need a report functionality, because I could technically delete problematic content myself through DBeaver.

But alas, Google Play Store had a different idea: it occasionally demanded app developers to push updates to their app's listing. Often, the demand is somewhat simple and goes along the line of "Please complete this declaration form so that we don't get sued in the EU," but occasionally, app developers have to upgrade the SDK that they built their app with.

To be clear, Apple isn't particularly nice to iOS app developers either, but they would generally leave you alone as long as you were paying your annual $99 fee (unless if you were doing something very naughty or some compliance thingamajig popped up).

I could just leave the warning alone and not update Billy. Occasionally, however, Google would threaten to close my developer account and unlist Billy if it hadn't been updated for awhile.

What this practically means is that I had to upgrade my React Native and Expo version, so that I could use the latest Android SDK as Google demanded me to. Otherwise, the new users that I've always yearned for would not be able to find Billy.

And boy, was it tough recently.

Upgrading React Native and Expo was horrible

...but not because of the upgrade process for RN / Expo themselves.

I don't know why, but the mobile world is very trigger-happy with breaking changes. I suppose it's for a good cause, because people's mobile phones are their gateways to the internet, so you don't want said gateways to be exploited.*

*But the cynic in me doesn't really believe that, because the web world seems to do just fine with almost never deprecating anything.

Unfortunately, one of the things that was included as part of the breaking change is something that NativeBase depended on: dismissing the keyboard when Android's back button is pressed.

|   |
|---|
|- if (enabled) {|
|- BackHandler.addEventListener('hardwareBackPress', backHandler);|
|- } else {|
|- BackHandler.removeEventListener('hardwareBackPress', backHandler);|
|- }|
|- return () =>|
|- BackHandler.removeEventListener('hardwareBackPress', backHandler);|
| |
|---|
|+|
|+ if (!enabled) return;|
|+|
|+ const backListener = BackHandler.addEventListener('hardwareBackPress', backHandler);|
|+|
|+ return () => backListener.remove();|
|+|

Normally, this would be fine because people would just complain on the library's GitHub Issues, and then the problem would be fixed by the maintainers of said library in a week or so.

Except this time, the library had been abandoned, as the creators of NativeBase rewrote the library into a new library called gluestack-ui.

I see their rationale in abandoning their project (NativeBase's performance was horrible due to its architecture), but frankly I disagree with outright abandoning any form of fixes, especially one that would fix their library crashing their users' app. I don't expect people to work for free, but I hope that they could at least allow their community to continue the maintenance of the library itself.

After the upgrade, my NativeBase text inputs had also started losing focus whenever I clicked on them, which prevented my users from typing anything (unless if they were quick enough). I don't know what happened, but this issue does not exist when I use the Input components from react-native directly.

So, as I don't wish to wait on an update that would never happen, I started to migrate away from NativeBase. And for the parts that I couldn't migrate, I patched the library myself using pnpm patch.

This could happen again, so I'd avoid depending entirely on component libs

In the web world, component libs can function normally in perpetuity because no-one ever dares to deprecate anything (lest they get an angry letter from Bank of America or something). There's no "web marshal" that dictates which ECMAScript standard you should adhere to - all versions of the standard are supported by modern web browsers.

So breaking changes would only happen if you're actively looking for it, e.g. if you decide to upgrade Angular 1.x to 2.x (sikes, they're actually completely different haha!).

But in mobile app development, Google and Apple aren't as afraid to deprecate things, because they're effectively monopolies of their own ecosystems. There's no incentive for them to be kind to mobile app developers, because there's simply no alternative (unless if you'd like to limit your app's market size to a bunch of cypherpunks dwelling on the edge of TOR).

Since things do get changed, especially things that interact directly with Android / iOS (e.g. gesture control, notifications), you have to be careful about bringing in component libraries, because there's a chance that Google / Apple would introduce a change that would break your component library.

What's the solution you ask? Style it yourself.

In hindsight, I don't really regret picking a component library, because I was ass at working with styles in React Native. I was simply unlucky that the component library that I happened to pick - NativeBase - was abandoned by its developers, and no easy migration path exists (unless if I want to refactor everything and risk breaking a bunch of UIs in the process).

Unlike the web, however, React Native apps are generally much easier to style. I suspect it's because their defaults are saner than most (e.g. not having Times New Roman as the default font), so you don't actually need to customise much. And the StyleSheet.create api is pretty much conceptually equivalent to CSS-in-JS, so it's been quite easy to use.

On that note...

Wow, the web app world has done something better than the mobile app world

Web development doesn't generally attract the cream of the crop when it comes to quality, because it's really easy to call yourself a web developer after having handwritten HTML code. On the other hand, mobile devs have to deal with waaaay more bullshit than their web dev counterparts (like paying for a membership before they could get their mobile apps in front of anyone).

But my God, web development has done backward compatibility right. It impresses me to no end that, for all intents and purposes, websites built in the dotcom boom can still function today (unless if they need Flash, in which case RIP).

On the other hand, it's most likely impossible to run Instagram's original mobile app - the one with the skeuomorphic camera as its icon - on your iPhone 17. Ironically on the other hand, it's probably still possible for your browser to run Instagram's original web app.

I feel like I have to make this disclaimer twice: the mobile app dev landscape is a completely different beast to web dev, so I don't expect it to be the same. And frankly, ECMAScript being 900+ pages long is probably a testament to how web dev isn't the shining example of any kind of app development.

But if there's a thing I love about web development, it's that I don't have Google and Apple breathing down my neck about updating Billy, even when it works just fine on my family's machines.