Skip to content

Instantly share code, notes, and snippets.

@walaura
Created June 8, 2019 16:38
Show Gist options
  • Save walaura/75202cef2e8a6e311d0862a1db40028e to your computer and use it in GitHub Desktop.
Save walaura/75202cef2e8a6e311d0862a1db40028e to your computer and use it in GitHub Desktop.
# I picked up React Native as a web developer and here's what I've learned
For the last couple weeks, I've been building a RN app at [work](https://gu.com). It's a news reader (duh) and a bit of a monster at that, with filesystem access, background downloads, and push notifications.
This wasn't my first time using React but!! It was my first time using RN. Which is scary because new things are scary. It's being a great experience though, and I'm kinda waiting for an 'OH S**T' moment where something unexpected goes horribly wrong – but so far it's been ridiculously fun.
Wy React Native? Well, my team originally wanted a PWA! We changed course for three key reasons:
- Despite web being a 'nice to have' our first market was app stores
- We wanted it to have very elaborate offline & background functionality. This is very early & experimental on web but a solved issue on mobile apps since day one.
- We wanted to deliver a native-like experience. Think 60fps animations, multiple stacked views, the works. These are solved problems in the app world too but on the web we are on our own.
- With `react-native-web` we have a path to turn this back into a PWA if needed
## It's not the web
On the web, standard React eventually generates an HTML-based website. This is how you can use plain CSS and by using `refs` you can directly call DOM functions on your components.
Native is a bit of a different beast. Despite using React's syntax – and unlike libraries like Cordova – RN never gives you HTML, or DOM Elements or CSS, but rather orchestrates native views directly on your mobile OS. This is pretty awesome!
Native UI is incredibly optimised and runs like a charm, and if you are used to struggling to get 60 fps animations on the web this is a whole new and incredibly refreshing world where you get that for free! (even on old as heck devices - more on perf later)
<img src="https://media0.giphy.com/media/DjEysmrFX7S8w/giphy.gif?cid=790b76115cfba11a516c764845cde8a3&rid=giphy.gif" />
## Getting started
I had some incredibly basic Xcode experience from doing prototypes eons ago (back then xcode looked like itunes? it was a weird time) but anyway I kinda knew what to expect compared to the web – faster apps, but a slower dev cycle with harder to use devtools.
i
was
so
wrong
First of all, if you just wanna dip your toes in the native waters you don't need any of this, you can use [expo](https://expo.io) to run your javascript and handle all the app-y bits. This gives you significantly less control over the app bits on your app but what's pretty cool is that all your code is still vanilla React. If you ever need that control you can just `expo eject` at any point and get your raw Xcode and android studio projects.
Even after you eject, you still won't be using Xcode or Android studio for the most part (unless you wanna). `react-native run-ios` will fire up a simulated iPhone X and run your app, and `react-native run-android` will install it directly to your phone that you only meant to charge but it's fine I guess now you've got an app on your phone.
The [react docs on setting up Android Studio](https://facebook.github.io/react-native/docs/getting-started.html) are pretty good. When it comes to iOS, code signing your app is a [bit of a pain](https://facebook.github.io/react-native/docs/running-on-device) – you need to do this before running it on a iOS device. You don't need to be a paid member of the apple developer program to do this but you need to be signed in into Xcode. What i normally do is try to compile it, click on everything red, and click the 'Fix issue' buttons until there are no more issues.
Finally, when running your app you can shake your device or simulator to get a pretty cool debug menu. You can hot reload code just like on the web, run the chrome devtools to hunt for bugs, or even open up the worlds cutest little inspector:
By the way! Everything in RN is a `View`. This means it's [super important](https://facebook.github.io/react-native/docs/accessibility) to mark up the semantic purpose of your views for a11y purposes. You can use `accessibilityRole` to do that. If you need alt text, `accessibilityLabel` has you covered.
## CSS
You will probably want to style your app. Unless you are making a todo list or whatever you will probably want to style your app _a lot_.
React native comes with a built in `StyleSheet` module. it handles styling for you. This rules because you don't have to argue ever again about what css-in-js solution to use. It's also bad because `StyleSheet` is _so similar_ to CSS you might think you are writing CSS.
The built in documentation on how to style things is [very good](https://facebook.github.io/react-native/docs/0.59/flexbox) but I wanna get into the big changes first
### It's pretty much like css-in-js
Your styles are a javascript object with camelcase properties. If you have used `emotion` or `styled-components` you'll feel right at home with this way of working
### But everything is a flexbox
Since all `StyleSheet` does is talk to the underlying OS you are restricted in ways you can layout compared to CSS. If you want to float something (For example to wrap an image to the side of some text) you are completely out of luck. Same goes for using CSS grid!
You have a magical `flex` property that consolidates `flexGrow`, `flexShrink` and `flexBasis` into a single number. I have no idea how to use this. [@NikkitaFTW](https://mobile.twitter.com/nikkitaftw) calls it 'backwards flex'. She has no idea how to use it either.
### So you can't float things
Ours is quite a special case but since our app had to render very type-heavy articles. To fix this we decided to render the body of the article in a webview and put that inside our react native app. This felt wrong and counter intuitive since "it's all javascript anyway" but it's important to always use the best tool for the job and the web was built to render documents!
### Or debug layouts 😰
Remember when you had to start coloring divs red to see where your layout had issues? Get ready for some NOSTALGIA. RN does offer a built in inspector but because it's inside the simulator (or inside your phone) it's kind of a hassle to use.
### And there's no cascade or selectors
You apply your styles directly to your components. You can't style children based on their type or have things like `hover` or `disabled` states or `:before / :after` pseuds.
This sounds super limiting but in reality having a well architected and modular app with small components will take care of _a lot_ of this for you.
None of your styles cascade, this can make your CSS more predictable but also a bit of a pain. We remediated this by using react context to encapsulate style properties we wanted to cascade down like theme colors. Context is ideal for this because you can have multiple contexts in the same screen for different nodes, almost working like css variables.
The loss of the cascade is not that big of a deal as it might seem except for a single but very important use case:
### Text
All text you want to render in React native has to be `<Text>Wrapped in a text tag</Text>` and it will display in the system font at 16px.
You can of course style your text to have any font and size you wanna, but text comes so many shapes and sizes that you should be prepared to have a ton of variations. In our app we ended up having a single file for all our styled text elements but I'm not sure this is the best structure.
You'll probably wanna use custom fonts! Especially now that all apps are white on black with a bunch of lines and there is literally no other way than type to tell them apart. This is where loading fonts comes in! And you don't have to deal with `@font-face` rules which is pretty neat!!
Sadly everything else [is pain](https://medium.com/@mehran.khan/ultimate-guide-to-use-custom-fonts-in-react-native-77fcdf859cf4). Your fonts will live inside your Android and iOS projects and here's where it gets hairy: To use a font in Android you will reference its filename, to use it on iOS you will reference its Postscript name. Don't know what that is? Don't worry, I didn't either. It's this thing:
/Screenshot 2019-06-08 at 1.14.29 pm.png
## SVGs
You don't get to use normal SVGs in your app. they are not supported by the image element. This is obviously very bad especially for icons and such.
For complex shapes and such you can convert them into bitmaps, 90s style. You'll probably wanna set up a build pipeline to churn them out for you. All the assets in your app will be downloaded upfront so file size is not as big of a critical consideration as it is on web (but don't go bananas!)
If you want to remotely import SVG that's a bit trickier but not impossible! There are [several libraries](TODO) that will do this for you by essentially chucking them in a webview.
For everything else (I'm doing this!) You can use `react-native svg` to use SVGs inside your code. The way this works is it exports react native versions of everything in an svg and you can use these and it draws the proper views for you
```
code sample
```
Having SVGs be first class citizens in React with props and animation and everything has changed the way i see all SVGs. i always knew they were markup but having to directly adjust them myself now has given me lots of ideas for cool things I can do with them.
At the end of the day `react-native svg` is a very elaborate hack that gives you views so it can also be used as a low level drawing library for things like lines and circles and whatnot! Your imagination is the limit!
A good way to assess what SVG loader to use is by asking yourself _how messed up will things be if this doesn't load?_ so for example you might want icons to be inline SVGs but big hero images to be remotely downloaded. Be aware that some things will _always_ be messed up and that some of your users will never see images anyway because they use screen readers or have poor eyesight or they just can't figure out what an arrow coming out of a box in a circle is supposed to mean.
Always make sure you have proper accessible descriptors for all your images! And provide sensible fallbacks if an image can't load (For example, in a hero, code in a background color that gives the text enough contrast)
## Animation
There's no CSS so there's no easy one line css animation or transitions. Sad :(
If you have tried to animate things using React on the web you probably already know it's hard. Animating things in react native is harder but also more rewarding??
If you want a specific effect chances are it's been done already. React-navigation does pretty serviceable transitions and you can even upgrade to connected animation using TODO. If you need even more all of this is built over the same [built in animation library](TODO) you can use.
The core concept of `Animated` is that you have 'values' you can update either as a result of user actions (like a drag), using time, or just updating them yourself. You can plug those values as `style` properties and RN does all the heavy lifting for you and makes things buttery smooth.
```
animation example
```
## Navigation
`react-navigation` kinda sounds like the `react-router` of this land. You might have noticed that mobile apps have more advanced navigation types than the web. You can't just replace things in place and call it a div, if you look at any mobile app, all your screens slide out and in and away. `react-navigation` has a [data model that is super linked to these transitions](https://reactnavigation.org/docs/en/modal.html).
Your navigators are a flat list of screens with an entry point and each navigator defines the transitions between its screens. For example you can use a single navigator for all your app and all your screens within it will do that thing where they progressively pile on top of each other from left to right.
But say you are doing a music player and you wanna add a card that can slide over any views with some "now playing" info. You can just create a new top level navigator that contains your original navigator and that lonesome card. You can even just use `{mode: 'modal'}` on it to get a pre made animation and voila, now if you navigate to your now playing view it glides over the rest of your app!
Something really cool is that even though your navigators are in a hierarchy your route names aren't. You can call `navigator.navigate(route)` from any route to any route without worrying about reaching out to the top level or something. It just works™.
For accessibility reasons you will probably want to use `<Link />` like [this](https://reactnavigation.org/docs/en/web-support.html). This will make things neat and tidy if you ever make a website with `react-native-web`
## Performance
The way RN works is that your javascript code runs on the device and tells RN what to draw on screen using native components. This means that creating views is expensive, but once they are on screen you are golden.
What this means in real world is that your performance gotchas are different. Your UI is always gonna be snappy, even on old devices, but it might start taking _a long_ time to initially render. Meaning you will tap on screen and wait a bit to see a new view pop up. You can remediate this a bit by using [screens](https://reactnavigation.org/docs/en/react-native-screens.html). Keep in mind that the debug build of react is SLOW compared to the production version! If you test your app on an old device make sure you are testing it in production before worrying a lot about it being slow.
Another thing to look out for is that event handlers are debounced and on older devices they can take up to seconds to register. If you have actions linked to scrolling (say, like moving to the next article if the user overscrolls the article they are on) this action may take a noticeable time to register, This is a blessing in disguise because you probably don't want to rely on swiping and panning as the sole UI mechanism for doing an action. Put some accessible buttons to do the action too!
## Typescript
Type safety is important! Especially if you don't wanna write tests. With that in mind it's important to have an open mind and remember that all typed JS compiles down to vanilla untyped JS when it actually runs.
This is important to keep in mind because the type definitions for React Native itself and many of its modules are pretty bad
If you wanna animate elements, `Animate` will cast them to `any`. Some of the type definitions will be outdated. Others will be typed but less 'safe' than you expected them to be (`react-navigation` doesn't know of it's own routes and takes any `string`)
Having full type coverage is a good tool for ensuring stable and maintainable code but at the end of the day it's not the only one. in my short experience, obsessing over it in RN apps is a quick path to frustration. Sometimes a response does just contain `any` and you have to deal with that.
@walaura
Copy link
Author

walaura commented Jun 9, 2019

ty!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment