A Deep Dive into React Native

Transcript from
React.js Conf 2015 Keynote 2 - A Deep Dive into React Native

by Christopher Chedeau.

I'm going to talk you about React Native.
So the reason why we built it is that we wanted to get the best part out native and we wanted to get the best out of web. When we did the transition from web to native we won at a lot of things.
The User Experience was better, the performance was better. But we also lost things at the same time, the developer experience was worst, we had to use imperative mutative APIs again, like the DOM, the compile time and linking time, the build time, all of those added up there were advantages we really wanted not to lose from the web.

So in this talk I'm going to tell you what makes an app feel native and I'm going to tell you how we did it in React Native.
So there are really three big pillars in React Native: the first one is

Touch Handling

This is, I think, the biggest thing that is different from mobile web. iOS and Android touch handling is much better.
The second one is

Native Components

It turns out that there's a lot of really high-quality native component that already exist, and people just try and try to reimplement them but they never feel as good as native. So we really want to be able to use those instead of trying to reinvent the wheel.
The third one

Style & Layout

And the reason why it is so important is because layout impacts so much the way you write code and its vastly different from the web to iOS to Android. So we want React Native to be
Learn once - Write anywhere
And so we also want to learn one for the Style and Layout.

Let's get started with Native Components.
The interesting thing about this Movie app demo is that everything that you see on screen is native but when you look at how it's implemented this is the main file

//[...]
var MoviesApp = React.createClass({  
  render: function() {
    return (
      <NavigatorIOS
        style={styles.container}
        initialRoute={{title: 'Movies', component: <SearchScreen >}}
        routeMapper={MoviesRouteMapper}
      />
    );
  }
});`

`var styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: 'white',
  }
});

module.exports = MoviesApp;  

and as you can see this is JavaScript, this is a React component (MoviesApp), there is no Objective-C here, but this component is actually a native one ().
So let's dig a bit inside of the file and as you can see

//[...]
var getImageSource = require('./getImageSource');

var MovieCell = React.createClass({  
  render: function() {
    return (
      <View>
        <TouchableHighlight onPress={this.props.onselect}>
          <View style={styles.row}>
            <Image
              source={getImageSource(this.props.movie, 'det')}
              style={styles.cellImage}
            />
            <View style={styles.textContainer}>
              <Text style={styles.movieTitle} numberOfLines={2}>
                {this.props.movie.title}
              </Text>
              <Text style={styles.movieYear} numberOfLines={1}>
                {this.props.movie.year}
              </Text>
            </View>
          </View>
        </TouchableHighlight>
        <View style={styles.cellBorder} />
      </View>
    );
  }
});

If you ever wrote any React code, this should feel super familiar, but there are still some differences, for example: you've probably never used this component nor this but if I tell you that they are the equivalent of a <span> and a <div> then you should be ready in a few seconds.
So how is this View component implemented?
Well let's open up Xcode and look at RKView.h which is a native one and as you cans ee it extends from UIView.

#import "RKView.h"
#import <UiKit/UIKit.h>
#import <FBReactKit/UIView+FBReactKit.h>
#import "UIView+FBReactKit.h"

@interface RKView : UIView

So this is actually a real native component that we are istantiating. Now if you look at RKUIViewManager.m and, how is it working? The way we do it is this (View) is a declarative API using React and then we're taking this (RKUIViewManager) and we're going to look at all that attribute that we can have and we're going to extract them and we're going to set them to iOS View mutative one, so this is the same way we implement <div> and <span> in React, but instead of having to output DOM commands we just output iOS native commands. What's interesting is this ViewOperation, this is done on the main thread so this is the only thing about React Native that's the main thread this is just setting the views so React, all the layout, everything, is done in a different thread and at the end of the diff algorithm all the React logic it's now flushed to the main thread.
So now you may wonder, how is it running?
Are we using JavaScript and transpiling into Objective-C or C? No we're not doing it.
So what we are doing is actually running a real JavaScript engine. In this case what we're doing is we're running a JS call which is the in the JS engine in iOS.
But let's try something: so we're going to reload Chrome in debug.
This is all time you have to wait for every single change using a normal native app.
So what is happening here: we are running the JavaScript, the React app for this, we are running it inside of Chrome, so Chrome is actually running the JavaScript so we're using v8 and all other things, and as you can see you can have all the React Tools.
What we're doing is: we have a WebSocket connection between the iOS simulator and Chrome, but this is like inside it to get better developer tools we could imagine, and it's already working, getting your iPhone connected via WiFi to your laptop and running the JavaScript that powers the app remotely.
And once that we can get this why not run it on the server for low end devices? Those are some crazy ideas that are actually possible, we don't know if it they are useful or not, but at least, we don't like to say no to people, and this allows us to say yes if this use case comes.
And one thing that would be cool is: what if we could server side render native apps? Maybe?
We could do it!

So this is just a simple app. Have you looked at the time it takes to reload?
Imagine the Facebook that takes multiple dozens of seconds in order to do any small change, we really want to fix this. So this is how we are using native components and then the next thing is you may wonder: okay we can render native components but how do you style them?
If you're coming from the web you're going to say use CSS. But unfortunately CSS are some issues, and we've been using CSS at Facebook for a long time and we've had to do a lot of workarounds around it, and it turns out that we believe that there's a better solution and it is writing CSS in JavaScript.
I did this talk two months ago at NationJS and this was the reaction on Twitter. But, we're not stopping to get used to it, this was the exact same reaction that we got when we opensourced React.
People complained that now you're colocating your mark up with your logic in JS. For the same reason that it turned out to be a good idea we believe that colocation your style with your JS is a good idea.
So those are all the issues with CSS. Qt first I was like: writing style in JS isn't going too well, using it, it turned out all those were solved without hacks.

So how is it working? This is how you declare your styles.

var styles = {  
  movieTitle: {
    flex: 1,
    fontSize: 16,
    fontWeight: 'bold',
    marginBottom: 2,
  },
  movieYear: {
    color: '#999999',
    fontSize: 12
  },
}

If you ever written any JavaScript this should look super familiar, you shouldn't be lost from CSS, but there are a couple of differences, you have to put commas instead of semicolons, nothing really big, but what most people stressed on this is: how do you use the styles? And this is it.
You're using inline styles.
So, it's been 10 years, and all the best practices stated well: do not use inline styles. So what changed?
Well I would phrase it otherwise. What's the difference?
This is how you write styles right now on React and so if you look at the two they are pretty similar, the first thing you'll notice is that we're not using classnames anymore, no big debate about this anymore, we can just use 'style', and the next thing which is a lot more fundamental is look at movieYear: this is a string, and why would we want to do a string, the string it has to be a reference to something in the CSS, and it's global, this is there because we were separating technologies: JavaScript and CSS.
What we should do is to separate concerns and so we still have this separation of concerns with this because the actual styles are separated from this call but we can use all the power of JavaScript, this is just a JS Object, you can use all the functions to generate those, you can use variables, we just got them in CSS, you can exports, you have a module that shares all your variables and everything, we believe that this is a much better thing.

But this is only one part of CSS that we ditched, CSS is actually much more than that, I would say it's not modular for all that means, CSS also has a big property which is layout.
If you take this screenshot and you put yourself in the GPU's shoes everything is just boxes full of pixels, the goal of the Layout it's to build those boxes, its only goal is to compute four values: Top, Left, Width and Height, and that's it.
It turns out there's a lot of ways to do it.
We first looked at how iOS does it because we wanted to be native and the first thing is you can implement a method called sizeThatFits. What it requires you to do is to write code that computes those four attributes (Top, Left, Width, Height) for every single element that you see on the screen. iOS developers have to write pages and pages of just boilerplate code just to display simple things.
The next thing that iOS has is Auto Layout. Auto Layout is using a constraint solver, and they sold it in a shiny box, but if you think about it, since Auto Layout is a constraint solver so you've got to define linear equations and you still have two sets: Top, Left, Width and Height in equations on all of the elements that are on the screen. So if you're building applications using it then you don't get that much of boilerplate but unfortunately linear constraint solvers cannot express everything that you want, for example it cannot express line breaks, so you can not have multi-line encoded in a linear constraint solver. There are ways to work around this but all of these ways, they loose the mathematical properties that is going to give you the absolute best layout, which is why use the constraint solver first place.
We didn't find anything that we really liked in iOS so we turned back to the web. So the first thing on the web is the CSS box model, and this is the best way to do layout I found.
Just with these 3 simple attributes: Margin, Padding, Border you can express the layout for every single element you want, very easily. But if you want to layout multiple elements: this is insanity.
For instance try to vertically center something with CSS in multiple columns, with floats and tables...
But it turns out there's a better way: which is

Flexbox

If you've never used Flexbox you should try it.
Let's take an example. We've got this and as you can see there are two columns, and the text is vertically centered.
I would challenge you tonight to try to implement this using CSS, and let me know it goes.
But with Flexbox is actually super simple just do alignItems center to vertically center them and flexDirection row to layout things in in multiple columns.
I'm a big fan of Flexbox but if you think about it React Native is a JavaScript engine for the native components and nowhere in there is Flexbox.
Because when you are on the web, Flexbox is implemented inside the browser, we don't have a browser, we don't have a WebView, so how do we implement this?
Well, we are all developers: we fire Jasmine and we start writing some tests. What the layout engine does is: it takes a tree of styles and it outputs a tree with the same shape but where all the values are just four numbers: Width: Height, Top and Left.
I started writing some test like this to implement Margin, Padding, all the Flexbox attributes but at some point I didn't know if what I was implementing was correct. So since we're in the browser, in this particular example, we can just take this tree as input and create actually real DOM elements and put some styles and we can send them to the browser and ask the browser to computes for us the Top Left Width and Height.
So I did this for a while but at some point I couldn't find examples where my algorithm was failing, so I thought I'd generate a bunch of tests.
The right thing with this is I can just generate a lot of frontend test, send them to my implementation then through the browser and see if it passes or fails.
So the workflow was the following: I would run the tests, find the first that failed, I would fix, iterate, find the first one that failed, fix it, iterate, and at some point there would be no failing tests, and so at this point I was confident that the implementation was right for all the attributes that it supported, and then I added new attribute and I did it again.
This way I was able to reimplement Flexbox, with all the attributes that we wanted, in about 2 weeks, which is crazy.

I want to put emphasis on the fact that we only really implemented a subset of CSS and the reason why is CSS has a lot of things in it and a lot of legacy things that we may or may not want to support, and so we focused on only the things that we know are useful for the developers and only the things we know we can implement fast.
So at this point we had Flebox implemented that was working right in the browser. But this is React Native, we want to use it in iOS, so how do you do that?
The first idea that comes to mind is: we're going to take Flexbox, take the JavaScript implementation and bridge it and execute like real time JavaScript but for doing layout you need to measure text and all the text measurements APIs are in Objective-C and we've got one very important prerogative for the bridge between JS and Objective-C, which is that: its asynchronous.
So we ported this implementation between the two, actually we will also port it to Java.
So now we've got the same layout implementation, running on all the three platforms. Since its probably useful to have any condition without any dependencies is on the browser we also opensourced it a few months ago
https://github.com/facebook/css-layout
so if you need to use it feel free to grab it.

How is it working in practice?
This is the MoviesApp and we've got a request from the designer that says 'I don't like the image to be on the left, I wanted to be at the top'. We fire up the editor and we see that it is actually using inline styles, and it is actually written in JavaScript in the same file, and using Flebox, so as a developer we should handle it with ease.
Instead of rows we want them in columns, after hitting save, it is going to instantly reload because we have file watchers listening. This is the promise of React Native: we want less than one second delay between modifying one line of code and show it to the screen.
If you compare this with the Xcode rebuilding that I showed you earlier, it was like 30 seconds for this small projects.

And it takes advantage of all the ES6 goodness.
After implementing the score the app broke, well what just happened? As you know with React we tried to do warning as best as possible and this is what we're doing for React Native.
If you have an exception we're not going to hide in the console, we're going to show it in your face! Internally we like to call it the Red Screen of Death.
What we really want with React Native is that if your familiar with JavaScript and React you should be able to get started very easily and should get like the same developer experience of the web.
What you don't see here is that all the JavaScript that we ran is being using ES6, it's compatible with anon modules so if you want to use underscore, or the Parse's SDK or whatever you just have to do is NPM install and if it doesn't have dependency on the browser it's just going to work.

At the end of today we are going to give access to a private github repo that contains React Native.
Just a warning: what you're going to get is just the bare minimum to run this them demo app.
Because actually three weeks ago we didn't know we were going to talk about React Native, we didn't know we were going to open source it so we crunched for the past few weeks to get it really stable, but in the next few weeks we're going to have a lot more things.

Question time.

Q: What do you guys have in mind for getting started
A: The way you get started is with a simple Xcode project that you copy and paste that is already set up in the repo. About build tools that's something we're working internally.

Q: How do you integrate with platform specific APIs as ApplePay or anything
A: The way the bridge work is you can just expose a JavaScript function. We showed an example of a Component but you can expose any function that you want.
The only constraint is that the bridge is asynchronous.

Q: How use this for desktop apps?
A: Right now we're focused on iOS and Android but we're trying to thing bigger, but it's a matter of resources.
We're working on the infrastructure so maybe one year under the road... or if you're interested in spinning up the process for Desktop you can. Here the're nothing iOS specific, if you can bridge Components and Native APIs you should be able to do it.