WebCake

These days, it’s rare to find someone who would argue that Ionic is the best way to build a mobile app. I, like many people, use Ionic for PoC work. It’s a good solution for quick builds. And in the case of a recent project, it was a good solution to get something on my phone quickly so that I could see how it helped me – in this case – track spending during Voodoo Fest.

But the experience falls apart quickly. With any sort of complexity on screen, the interactions have a low threshold for lag. There’s much less flexibility for native integration and more reliance on resource-limited web-based solutions. I won’t go into the details, because there are dozens of blog posts covering this topic in depth.

As much as I’ve loved Angular and the tool sets built around it, when it came time to build a mobile app that I expect to have a considerable lifespan, the right solution was React Native.

This is not a rant on one framework over another. I’m not a framework fanboy. While I have many years of experience in Angular, it was a no-brainer for me to switch to React Native when I decided to take my PoC seriously, and I didn’t hesitate to do so.

This is a retrospective on some of the differences I found when building for React Native as opposed to Ionic. Some of the code for my PoC made it over without any changes, TypeScript and all. Other files (specifically the views) were entire rewrites. In addition, there were some genuine surprises in the RN toolkit and community that I’ll get into.

The Fundamentals

Is React Native really as good as it’s purported to be? Holy shit yeah. To put it mildly, I was pretty frustrated when I found myself constantly noticing increasing lag as I added new screens into my Ionic PoC. When I began the rewrite in RN, I noticed the interaction difference immediately. It was shocking how much smoother interactions were.

Additionally, as an RN developer, you’re forced to embrace the fact that you’re building for mobile, using code that truly must work with native code. Ionic tries to hide that truth by making it possible to never open Xcode or Android Studio while you’re developing and running in the simulator. RN is the opposite. Xcode is always open as I’m working, as I have to make sure libraries are referenced properly, installed in the right locations, etc.

The fundamental difference? With RN, I’ve been 100% aware, the entire time, that I’m building for mobile. With Ionic, and therefore Angular, the mental model was still web-based. While Ionic will allow me to get away with using web-based technologies in a mobile setting, RN forced me to accept that what I’m building will never run on the web. It’s a mobile app, and that’s it.

The Code

Since we’re talking about JavaScript (TypeScript), regardless of whether we’re writing in Ionic or RN, I was able to transfer over a surprising amount of code from the Ionic PoC to my RN implementation. BUT! There were plenty of hiccups along the way that caused me some true headaches.

Dependency Injection

Yeah, that’s a problem. DI isn’t built into the React universe, at least at the time of this writing. So dependencies have to be imported and instantiated within each explicitly.

For simple dependencies, that’s not a big deal. It becomes a big deal for a full dependency tree. In the Angular world, it becomes easy to take DI for granted and not know nor care how big your dependency tree can get. React (and therefore RN) end up challenging that mentality.

A different approach to singletons

The secondary consequence is that an Angular developer’s understanding of how to inject a singleton goes out the window. Fortunately, it’s still possible, and there are a lot of StackOverflow posts on how to do it. Here’s one: https://stackoverflow.com/questions/44719103/singleton-object-in-react-native.

TypeScript

These days, I can’t even write in plain JS anymore without getting a headache. Even my server-side code (or, especially my server-side code) has to be in TS.

Syntax

While a lot (and I really do mean a lot) of React and RN blog posts are written in plain JS, a keen eye for TS and the basics of React will be able to pick up on, for example, how a JSX factory function translates to a TSX class. But if you have any uncertainty about the relationship between TS and JS, it might be a significant challenge to adopt a more object-oriented approach to your code.

Mind you, factory functions are still fine in TS. It’s not what many of us who build in Angular are used to, but it’s a legit way to code, and can be done in a type-safe way. But you’ll have to add in the types manually. And that’s a time-consuming bummer.

Support

While the popularity of TS has surged over the past couple of years, there’s still some shaky support for TS in the React and RN community. I’ve never before had to use the // @ts-ignore comment; in this project I have 17 and I still haven’t hit what I consider to be an official v1 release.

Most of that comes from either out of date typings for libraries I’m using, or incorrect typings exported from a .d.ts file.

However! I will say that every library I’ve used does include at least some typings. So support is definitely getting there, even if some of those type definitions are out of date.

JSX and TSX

I was once told that an argument for using React over Angular is that JSX is much closer to raw HTML, and therefore less of a framework-specific way of writing markup. Let me be clear, now that I’ve been using it – that was a bold-faced lie. If people tell you that, they are lying to you. Case in point, here’s an example of what a loop looks like in JSX.

There’s nothing wrong with a programmatic syntax entering the template world. It’s pretty widely accepted as the way that we build templates, even outside of the front-end world. We of the Angular way are already used to this.

I have no hard feelings for the ways in which JSX/TSX do templates. In fact, I’ve found them pretty powerful. But I warn you not to be led astray by anyone who tells you that Angular templates have too much framework-specific syntax. React, by way of JSX/TSX, is also a very framework-specific in its templating.

The XML side of JSX

Like your typical markup language, JSX looks a whole lot like XML. In a web-based React app, you might go pretty far without noticing too much of a difference from HTML in your JSX/TSX templates. You’ll use web-standard elements and create your own reusable components where/when you need to.

In RN, that’s not the case at all. Again, you’re not running on the web; so you’re not using web-standard elements.

The good folks at Facebook, as well as the good folks in the RN community, have published a lot of pre-defined components for us to use in our RN app. It’s worth noting – that’s really all that we have available to us.

An RN app’s templates are built on top of the minimalist RN UI components. They have correlating web-based elements, at least to help us to wrap our heads around things a bit. A <View> is essentially a <div>, for example, in the RN world. But instead of using the web tags that we’re used to, we have to shift our way of doing things and, in a way, re-learn the template tags available to us.

Why? Because each one ties into a native code block that directs the native UI to do things. We can either build those ourselves (hard) or use what’s been provided (not as hard).

The result ends up looking much more like XML, at least in my mind:

<View style={[styles.layout]}>
  <IterationNavigator
    screenProps={{definitions: this.state.definitions, transactions: this.state.transactions}}
    navigation={this.props.navigation}
  />
  <AddTransactionComponent onPress={this.toggleModal.bind(this)} />
  <Modal
    animationType="slide"
    visible={this.state.modalVisible}
  >
    <View style={{marginTop: 22}}>
      <TransactionFormView
        dismiss={this.toggleModal.bind(this)}
        initialValues={{dateTime: new Date().toISOString()}}
        headingText={"Enter Transaction Details"}
      />
    </View>
  </Modal>
</View>

BUT! Since it’s really just a templating language with the full power of JS expressions baked in, you can get pretty expressive. The following hideousness is a single component implementation that I haven’t refactored yet:

<SectionList
renderItem={({item, index, section}) => {
  let itemStyle;
  if (index === section.data.length - 1) {
    itemStyle = [styles.item, styles.lastItem];
  } else {
    itemStyle = styles.item;
  }
  return (
    <TouchableRipple onPress={() => {
      ActionSheet.show(
        {
          options: this.actionOptions,
          destructiveButtonIndex: this.destructiveActionIndex,
          cancelButtonIndex: this.cancelActionIndex,
          title: "What would you like to do?"
        },
        buttonIndex => {
          this.handleActionSelection(buttonIndex, item);
        }
      )
    }}>
      <View style={itemStyle}>
        <Text style={styles.primaryText}>${item.amountSpent}</Text>
        <Text style={styles.secondaryText}>{item.category} | {item.vendor}</Text>
      </View>
    </TouchableRipple>
  )
}}
renderSectionHeader={({section: {title}}) => (
  <View style={styles.heading}>
    <Text style={styles.headingText}>{title}</Text>
  </View>
)}
sections={this.props.screenProps.transactions} />

Yet another benefit of TypeScript

There’s an added benefit if you’re using TSX. At least in WebStorm, as I’m using TSX elements in my templates, I get automatic syntax error highlighting if I forget to set a value on a required attribute.

Styles

If you’ve been following React for any length of time, then you’re probably familiar with the rapidly growing community of CSS-in-JS. If not, then this might seem a little weird.

There is no Sass

Maybe there’s a way to pull if off, I haven’t dug too deep into it. But from what I’ve seen, you won’t be writing style code in Sass (or LESS, for that matter). And that also means no mixins, including those you might have gotten used to over the years.

That’s not entirely a bad thing. The styles in a RN project are declared in JS/TS code, and so the programmatic aspects of writing said code has all of the power of JS/TS available to it. At the end of the day, Sass helped us get out of writing explicit key-value pairs in CSS code, and become more programmatic in our styling. CSS-in-JS does the same thing.

While that does mean that some of the programmatic bits that came with Sass now have to be coded by hand, it’s a pretty quick endeavor; at least it was for my app’s styling needs.

No specificity

You’re not working in a world of CSS specificity, so you are truly styling blank elements with whatever style code is applied to them. It’s more declarative, sure. But on the other hand, it means you won’t have to go digging through mountains of applied CSS in a web inspector trying to figure out what style declaration is screwing up your component.

Instead, we can use standard JS syntax to apply style properties, in an easy-to-read way:

const styles = StyleSheet.create({
  layout: Object.assign({}, LAYOUT_BASICS),
  loadingLayout: Object.assign({}, CONTENT_CENTER, P_1),
  loadingText: Object.assign({}, TEXT_CENTER, DISPLAY_4, M_T_2),
  buttonStyle: Object.assign({}, {
    maxHeight: 2 * VARIABLES.rem,
  })
});

And that’s it! Those are the only styles applied to the components that receive them. It’s a bit of a relief coming from an Ionic world, where the components that come with Ionic have some deeply nested styles that require a lot of work to overwrite without getting into the business of writing !important everywhere.

The Community

I’ve been aware of the fluidity and general infighting of the React community for years. For example, when Flux came onto the stage, it was a big deal and had a significant impact. Now if your project still has a Flux implementation, people are probably going to say you’re doing it wrong. There’s the battle over Redux vs. MobX vs. the Context API vs. (where my vote is) stateful services.

Once I got about a month into RN, I could tell that a lot of this comes from how generally wide-open React in general really is. To be clear, it’s extremely powerful and I’m glad I’ve had the opportunity to get to know how it works. But I was surprised that certain things we Angular developers take for granted were missing.

No built-in router

That was a shock. There are a few solutions out there, and a quick Google search will net you plenty of comparison blog posts. So I won’t go into them.

We of the Angular world are generally accustomed to a lot of decisions being made for us, including what router to use. Angular comes, out of the box, with almost everything you need to build a web app. And the community has generally embraced the decisions made by the Angular team. Admittedly, when ng2 came out, it was still considered reasonable to not use the CLI. But these days – the ng7 days – failing to use the CLI is a big taboo. The Angular community has become more cohesive, while I’m seeing the React community still scattered around tribes of toolsets, solutions, and patterns.

I ended up choosing React Navigation for my React Native project. Let’s talk about that for a second.

Outdated and abandoned

I’ve been shocked as to how many tools I’ve seen recommended in blog posts, just to find that the project is abandoned and looking for maintainers. One the one hand, that looks bad.

But on the other, I’ll point out that a lot of people have taken a real shot at open-source projects and even generated a bit of a following for their work. If they weren’t able to maintain them, that’s no fault of theirs. Life happens. Kudos to the React community for coming up with so many solutions over time, even if now those solutions are starting to reach the end of their lifespan.

Unfortunately, a problem still exists. Community bloggers might have been ecstatic about a solution a year ago that was dropped six months ago. And now because of a dependency problem the tool might not work in the way we need it to.

I’ve found myself doing a little more digging that I would have expected to find solutions that fit my needs, given the vast adoption of React and React Native across the developer world.

Additionally – and this is where React Navigation comes in – I’ve found that documentation isn’t always kept up to date outside of those tools built by Facebook. React Navigation has been somewhat slow in updating their docs from v2 to v3, which has caused a few headaches. Furthermore, the amount of examples out there are lacking – for React Navigation and for other libraries I’ve used. Many of the resources I’ve read through give high level explanations, but nothing low level enough to give a solid understanding.

The result is that I’ve found myself digging through source code a lot more than I had expected to. Again, maybe that’s a good thing. It’s on me to understand how the tools I use work; so a community that relies on reading source code can’t be wrong. But it takes time away from writing my own code, which can be frustrating.

The Takeaway

None of the speed bumps I’ve experienced along the caused enough of a problem to even have the slightest regret in choosing RN. Some of those speed bumps came from being an Angular developer, spoiled by a framework that made a lot of decisions for me. Others were caused by having to simple shift my mindset from the way I was used to doing things – e.g. Dependency Injection – to another way.

I highly recommend that if you’re an Angular developer, you take a strong look at RN for your mobile project instead of going straight for Ionic. The results will be immediately noticeable.

Leave a Reply

Your email address will not be published. Required fields are marked *

This site uses Akismet to reduce spam. Learn how your comment data is processed.