Upgrade to Pro — share decks privately, control downloads, hide ads and more …

Under the hood of React Native

Under the hood of React Native

Slides from my talk at the Reactive conference in Bratislava, Slovakia

Video: https://www.youtube.com/watch?v=8N4f4h6SThc

Martin Konicek

November 04, 2015
Tweet

More Decks by Martin Konicek

Other Decks in Programming

Transcript

  1. Martin Konicek
    Facebook, React Native
    mkonicek
    martinkonicek
    Reactive conf

    Bratislava, Slovakia

    November 4, 2015
    Under the hood of

    React Native

    View Slide

  2. Overview
    Intro

    Building the Ads Manager & Sharing Code
    Architecture & Extensibility
    Open Source Process

    View Slide

  3. React Native
    React Native is a library for building native apps with React. We are at a React
    conference and there were other talks about React Native, therefore I’ll assume
    you’re already familiar with React and basics of React Native.

    View Slide

  4. Let’s have a quick look at the main
    benefits of React Native compared to
    traditional mobile development.

    If you’re developing for the web, one thing
    you’re used to is that you can reload to
    quickly see your changes. You probably
    know that React Native gives you that
    same experience. Compare that to
    compiling and copying the binary to the
    device every time you make a change.

    View Slide

  5. The way this works is there is a local development server (a custom
    packager, you can imagine it as something similar to Webpack) that runs
    on your machine and serves a bundle containing all the JS needed to
    run your app. On a small change it can return a new bundle really fast.
    GET /index.bundle

    View Slide

  6. Standardized APIs

    Another big thing for me personally is that
    I don’t have to learn a completely different
    set APIs for Android and iOS.


    For example, I don’t need to learn twice
    how to write to disk, make network
    requests, or show a scrollable list of items.

    View Slide

  7. Standardized APIs


    ...

    ——————————
    styles = {
    row: {
    flexDirection: 'row',
    alignItems: 'center',
    padding: 5,
    backgroundColor: 'white',
    },
    };
    That’s also true for layout, which is done
    exactly the same way on Android and iOS
    using Flexbox that you already know from
    the web.

    View Slide

  8. Layout-only View Removal
    Before After
    What’s interesting is React Native uses
    one Android-specific optimization related
    to layout. These two UIs look the same
    but the view hierarchy on the right is
    simpler.

    View Slide

  9. Layout-only View Removal
    Before After
    Goal
    The way it works is we remove all the native views that only define layout but are
    not visible in any way (e.g. don’t have color). This means Android has to do less work
    traversing the view hierarchy which means a more performant UI.

    View Slide

  10. Building the Ads Manager
    Now that we’ve done a very quick intro to React Native let’s look at how we built
    the Ads Manager. This was the first app built fully in React Native both for iOS and
    Android.

    View Slide

  11. Here are some screenshots from the Ads Manager on iOS.

    View Slide

  12. And here’s what the Android version looks like.

    Both apps have native look and feel, I encourage you to try them out. You’ll need to have ads on
    Facebook to have the full experience but you can play with it even without having ads.

    View Slide

  13. 3 month iOS release→Android release
    Same dev team
    85%+ code shared
    What was the experience for the Ads Manager team like?


    It only took them 3 months to build the Android app once the iOS version was finished. It was one
    team of people building for both platforms. Most people on the team had mainly JS experience,
    some iOS and some Android experience.


    It turned out they could even share most of the code between platforms. Note that code sharing
    wasn’t an explicit goal - React Native is learn once write anywhere, not write once run anywhere.

    View Slide

  14. And here is how the Ads Manager team felt about that.

    View Slide

  15. How do you share code?
    OK, even though sharing code wasn’t an explicit goal it sounds really cool.
    How do you do that?

    View Slide

  16. AdDetailView.js
    The easy case is when a part of
    the UI looks and works the same
    way on iOS and Android.

    Like in this screen, where we’re
    sharing all the UI and data
    fetching logic.

    View Slide

  17. AdDetailView.js
    But within a shared JS file you can still
    check what platform you’re running on.
    On this slide notice another interesting
    thing - AdsManagerText - a component
    that defines a consistent look for text
    across the whole app, making it easy to
    change it in one place.

    View Slide

  18. To take another example, look at
    the main Ads Manager screen on
    iOS and Android. There’s a
    ScrollView that has the same
    contents on both platforms but
    you can see that it has different
    pull-to-refresh behavior, specific to
    the platform.

    View Slide

  19. RefreshableScrollView.ios.js
    On iOS, the implementation might look
    like this, where we put an Activity
    indicator above the ScrollView. (This is
    just a simplified example.)

    Note the .ios.js file extension.

    View Slide

  20. RefreshableScrollView.android.js
    On Android we use the standard Android
    view called SwipeRefreshLayout.

    View Slide

  21. CampaignsView.js
    Finally, since the RefreshableScrollView
    has the same API on both platforms we
    can simply use it on both platforms.

    View Slide

  22. How does it work?

    View Slide

  23. GET /index.bundle?platform=ios
    GET /index.bundle?platform=android
    You saw at beginning of the talk there’s
    the packager which serves JS to your
    application.

    View Slide

  24. .js, .ios.js
    .js, .android.js
    The packager chooses the JS files based
    on the platform when creating the
    bundle. Simple.

    View Slide

  25. Architecture

    (Android)
    iOS: @tadeuzagallo
    Now we’ll go a little bit deeper and look how React Native runs your code.

    It’s important to note I’ll focus on Android here but the iOS architecture is very similar.
    You can read about the iOS architecture in a blogpost by my colleague Tadeu.

    View Slide

  26. Native JS VM
    Java/C++
    Bridge (C++/Java)
    At the core of React Native is a bridge that lets native code calls JS and vice versa. 


    There’s a JS VM (we use JavaScriptCore) running your code. On Android, JavaScriptCore
    needs to ship with your application which means a Hello World Android app is about
    3.5MB. On iOS JavaScriptCore is part of the system.

    View Slide

  27. Native JS VM
    Java/C++
    Bridge (C++/Java)
    A common question I get about the architecture of React Native is whether the
    application code runs in a WebView. There is no WebView, it’s JS running in a VM and
    controlling native UI.

    View Slide

  28. Java JS
    Bridge
    Now let’s zoom in. This is the same as
    on the previous slide, native on the left,
    JS on the right.

    View Slide

  29. Java JS
    Bridge
    AppRegistry.runApplication(  
    ‘MyApp’,  
    new  WritableMap());
    require(‘AppRegistry’).runApplication(‘MyApp’,  {});
    [1, 1, [‘MyApp’, {}]]
    It’s important to realize that we always
    start in native. In this case native
    decided to start your app. It sends a
    function id and arguments to JS using
    our custom JSON-based protocol over
    the C++ bridge.


    View Slide

  30. Java JS
    Bridge
    require(‘AppRegistry’).runApplication(‘MyApp’,  {});
    UIManager.createView(2,  ’View’,  {…});

    UIManager.createView(3,  ’Text’,  {…});


    UIManagerModule#createView(2,  ‘View’,  …)  
    View  newView  =  new  View();  
    UIManagerModule#createView(3,  ‘Text’,  …)  
    TextView  newView  =  new  TextView();  

    [[2, 3, [2, ‘View’, {…}]],
    [2, 3, [3, ‘Text’, {…}]]]
    JS calls that function which in turn leads
    to a bunch of calls from JS to native.
    For example, create a TextView, send a
    network request. These are all batched
    together and sent back to native
    asynchronously.


    View Slide

  31. Java JS
    Bridge
    require(‘AppRegistry’).runApplication(‘MyApp’,  {});
    UIManager.createView(2,  ’View’,  {…});

    UIManager.createView(3,  ’Text’,  {…});


    [[2, 3, [2, ‘View’, {…}]],
    [2, 3, [3, ‘Text’, {…}]]]
    The calls are batched together so we
    don’t pay the overhead for each
    individual call. However, in some cases,
    such as when JS is doing lots of work, it
    might be better to split the batch into
    several batches. Imagine JS wants to
    create a few views, then read something
    from disk and do lots of computation,
    then update more views. It can be better
    to flush the queue of calls (see
    MessageQueue.js) into native early - this
    way the person using your app can see
    something on the screen meanwhile JS is
    still doing work.

    View Slide

  32. UI Event Queue JS Event Queue
    Native Modules
    Event Queue
    In the previous slide we said the calls between native and JS are asynchronous. To explain that let’s look at
    the threading model.


    There’s the main thread with a queue of events. This is the Android main thread. Then there are two
    additional threads - one that runs operations on native modules and one that runs your JS (on iOS this
    works a little bit differently, refer to Tadeu’s blogpost). Each thread is processing a separate queue of events.

    View Slide

  33. UI Event Queue JS Event Queue
    Native Modules
    Event Queue
    Touch Event
    Now imagine the OS told us (on the native main thread) there’s a touch event.


    The touch event here serves just as an example. If you are interested in how touch handling works refer
    to the awesome talk by @alex_frantic in the Videos section on the React Native website.

    View Slide

  34. Handle Event

    -> bridge
    -> Runs JS
    Touch Event
    Touch Event
    UI Event Queue
    Native Modules
    Event Queue
    JS Event Queue
    Based on the touch event we add
    an event to the JS queue. The JS
    thread synchronously calls your
    application code via the bridge.
    This runs your JS which calls e.g.
    setState and render.


    View Slide

  35. Handle Event

    -> bridge
    -> Runs JS
    Handle Event

    -> bridge
    -> Runs JS
    Touch Event
    Touch Event
    UI Event Queue
    Native Modules
    Event Queue
    JS Event Queue
    Dispatch View Updates
    Update UI Dispatch View Updates
    Update UI
    As you saw in the bridge overview, your code returns operations to be done by native modules. These can
    be things like adding, removing or updating native views, sending network requests etc.


    Then on the native modules thread we calculate new layout and finally update the views that need to
    change, on the main thread. Why not do layout in JS? We need to measure text - only native can do that.


    The whole cycle from the touch event to updating the UI should ideally happen within 16ms: this is how JS-
    driven animations work too but instead of a touch event there’s a timer event that fires on every frame.

    View Slide

  36. Here’s an example of an animation created using the Animated API.
    Every frame a timer fires in native, JS computes new positions and
    tells native to update the views.

    View Slide

  37. In some cases when JS is busy doing other work this can cause
    dropped frames. A workaround for this is to use the
    InteractionManager to delay work until animations have finished.


    In the future we might look into offloading animations created
    using the Animated API to run entirely in native.

    View Slide

  38. Modularity
    Another important thing to know about the architecture is that it is very modular.
    There are two important abstractions that define what’s exported from native to JS:
    Modules and View Managers.

    View Slide

  39. Modules

    React.NativeModules.Dialog.show(‘♥  Bratislava’)
    DialogModule#show(“♥  Bratislava”)
    =>
    A module has some state and methods that JS can call.

    In this example, we’re calling a Dialog module in JS which translates to a call on the
    native dialog module in Java.

    View Slide

  40. View Managers
     

    =>    new  TextView(getContext())  
    =>    new  Switch(getContext())
    View managers define how JS views map to native views. When you specify you want
    to render a Switch in JS there’s a View Manager that knows how to do that and
    creates an Android Switch.

    View Slide

  41. View Managers
     

    =>    new  TextView(getContext())  
    =>    new  Switch(getContext())
    The coolest thing about this is that you can easily define your own view managers
    and modules. This is my other favorite part about React Native - you can simply
    drop down to native code when needed.
    … define your own

    View Slide

  42. Here is the code of MainReactPackage. It
    simply defines modules and view managers
    that are available to any app. You can see
    things like storage, network, Image or
    ScrollView.

    View Slide

  43. MainActivity.java
    Let’s say you’ve implemented support for native maps.
    To start using the new native feature in your app, create
    a simple package with you view manager. Then simply
    add that package to your app’s MainActivity.

    View Slide

  44. https://react.parts
    And what’s even cooler is you can
    then publish your package to npm
    and register it on react.parts to make
    it easily discoverable. Then everyone
    can find and use your code in their
    apps.


    The process of creating and linking
    native modules could be made
    smoother, we plan to work on that.

    View Slide

  45. Open Source
    This brings me to the next topic which is our Open Source process.

    View Slide

  46. If you look at our github repo an interesting thing to notice is we’ll
    soon have 400 unique contributors to the project.

    View Slide

  47. Releases
    The first important thing to understand about the Open Source process
    is how releases work.

    View Slide

  48. 2 weeks of development
    release branch: e.g 0.14
    2 weeks of stability
    master
    publish to npm
    e.g. 0.14.0-rc
    Every two weeks we cut a release branch from master and publish to npm so you can try
    out the new release candidate.

    View Slide

  49. 2 weeks of development
    publish to npm,
    e.g. 0.14.0
    release branch: e.g 0.14
    2 weeks of stability
    2 weeks of development
    master
    publish to npm
    e.g. 0.14.0-rc
    We cherry-pick bug-fixes and sometimes small features from master during the following
    two weeks, then publish to npm and the whole process repeats.

    View Slide

  50. For every version you can find release notes on github.

    Thank you James Ide (@JI) for taking the time to write these!

    View Slide

  51. Exporting commits to github
    The next thing that’s useful to understand is how we sync code
    between the internal fb repo and github.

    View Slide

  52. If you go to github and look at the commits, you’ll see commit messages
    like this one. What are the differential revision and sync id?

    View Slide

  53. To explain that we first have to explain
    how we store the code internally. The
    source of truth for the React Native
    codebase is an internal Mercurial repo
    called fbsource. Here’s a simplified
    illustration of its layout. The react-
    native-github folder is a copy of what
    you see on github.


    We also have some Facebook-specific
    extensions like error reporting to the
    Facebook backend and of course there
    are apps like the Ads Manager, for
    example.

    View Slide

  54. The way we commit code internally
    is we use a tool called Phabricator.
    Each code change is submitted to
    Phabricator and is called a “diff”. It is
    very similar to a pull request on
    github. A diff has to be reviewed by
    at least one person.


    Phabricator is integrated with our CI
    system - what you see at the bottom
    are test results. A lot of these tests
    are commit-blocking.

    View Slide

  55. Once a diff has been committed
    (AKA it “has landed”) we create a
    patch only for those files that are
    open source (in the react-native-
    github folder), apply this to the
    github repo and push. This is done
    by a job that runs every minute.

    View Slide

  56. And again, here’s what the diff looks like once it has been landed
    and exported to github.

    View Slide

  57. Pull Requests
    OK, we’ve talked about getting code from the facebook codebase
    to github. What about the other way?

    View Slide

  58. Here’s what happens when you send a pull request.

    View Slide

  59. We have a bot that looks at files touched by the pull request and
    mentions people who are likely to be the best reviewers for the PR.

    View Slide

  60. Once the code review is done and we’re happy with the PR, we comment
    ‘shipit’ which imports the code into Phabricator.

    View Slide

  61. Here is a diff created from that
    pull request.

    The most important thing in this
    slide are the Ads Manager tests.
    A pull request can affect closed-
    source apps and we need to
    make sure everything works
    when we merge the code into
    the fb repo.

    View Slide

  62. Once the code is merged internally it gets exported to github like any other commit,
    keeping the original author of the PR which is important.

    View Slide

  63. And as you can see people were happy this one got merged

    View Slide

  64. SUMMARY
    To sum it up: A pull request gets turned into a diff, is tested and merged into
    the fb codebase and then exported to github like any other internal commit.

    View Slide

  65. How to help

    View Slide

  66. If you want to send a PR and looking
    for a place to get started, look for
    issues labeled ‘Good first task’.

    View Slide

  67. You’ll probably notice the number of issues is too high. One reason is a lot of those
    issues are really questions. It would be awesome to have a way to put a banner on
    github saying those questions are best asked on Stack Overflow.

    View Slide

  68. Please use Stack Overflow to ask & answer questions. It’s a perfect product for this.

    View Slide

  69. Another thing about github issues is that
    they have no voting system. We’d really
    like to know what the most important
    things are for you guys. We started using
    a website called Product Pains.


    You help us by voting, submitting new
    issues there and above all helping fix the
    most important ones.

    View Slide

  70. You can see that the top one is to get
    Android to feature parity with iOS. This
    means open sourcing more of the views
    and modules we’ve been using internally. 


    To do that we need to clean up the APIs
    and add good examples. I’m working on
    this.

    View Slide

  71. Another thing you can help us with is documentation.


    Here’s an old version of documentation for one of the most common things people try to do
    - using an Android device for development. After a few conversations on github and Twitter I
    realized:

    View Slide

  72. .. these were the points where it was easy to get stuck. It’s important to note this particular
    part of the docs has been improved only thanks to the feedback on Twitter. We’ve written the
    Android docs but we are Android engineers so it’s often not obvious what’s difficult. You guys
    are in the best position to tell us what needs to be clarified.


    Tweet at @martinkonicek or even better send pull requests that improve documentation
    every time you get even a little bit stuck. Happy to review them.

    View Slide

  73. Thanks!
    @martinkonicek
    Thanks to all of you, the community around React
    Native is just amazing.


    This is the project with the most potential I’ve ever
    worked on and I’m very excited to see what we’ll
    build together.

    View Slide