React Native:

How we do it

Adam Terlson

@adamterlson

Me

Adam Terlson

Lead Engineer @ InterNations

ASP
ASP.NET
.NET MVC + FE
Node + FE
FE

2.5 years

React Native

👶

InterNations

Global social network for expats

Events/activities

3 million users

100 employees

Existing Production Apps

  • Developed by external contractors
  • +50K iOS / +50K Android Users
  • Hybrid
MISSION:
SLAY THE HYBRIDS



Objectives:
  1. First class native feel & performance
  2. Android & iOS simultaneously
  3. Offline-first
  4. Support a broad range of features

EVENTS

SEARCH

MESSAGING

USERS

GROUPS

CONTACTS

MAPS

PUSH NOTIFICATIONS

ACTIVITY FEEDS

 

 

3 FE


2 BE

 

Me

?

solid code

Write

Prettier

Fewer keystrokes

Style consistency

eslint

Error prevention

 

Static Type Checking

is

CRITICAL

Flow

 

GOOD

Prevents many classes of errors

Significantly reduces refactoring effort

 

BAD

Library types are missing or outdated

Error reporting is bad (but somewhat consistent)

Doesn't play well with Saga

Issues are slow to get fixed

Random $#(%*

TRY - Flow's spread annotation (instead of union)

https://github.com/facebook/flow/issues/1326

 

FAIL - Eslint doesn't support the syntax
https://github.com/babel/babel-eslint/issues/443

 

WORKAROUND - Flow's comment syntax

 

FAIL - Prettier strips the comments
https://github.com/prettier/prettier/issues/204

Next time: Type Script

Step 2: Release 

Nightlies

 

Continuous Delivery

&

Jenkins

 

Code Push 

 

Fastlane

state?

State of JS - 2017 Survey Results

Redux

Standard, most popular solution

Lots of tooling, libraries

Functional in style

Rather boring

Actions

  1. Separate concerns
  2. Traceable side effects

Normalized Collections

idsInsert + idsRemove

 

byIdInsert + byIdRemove

 

byIndexInsert + byIndexRemove

case "MESSAGE_RECEIVE":
    return {
        ids: idsInsert(state.ids, action.payload),
        byId: byIdInsert(state.byId, action.payload),
        byConversationId: byIndexInsert(
            state.byConversationId, 
            action.payload, 
            (message: TMessage) => message.converationId,
        ),
    }

Selector Functions

  • Get a single value from state
  • Type annotated
  • Namespaced
  • Unit Tested
  • Cached

Mobile UI with React

Functional UI

Tweet

1: Presentational

UI Presentation

Stateless Functions that compute visible things:

 

View, Text, Styles, Animation, Static Text

 

No business logic!

Documented in Storybook

Not unit tested (shhh)

Code: Presentational

2: Higher Order Components

UI Behavior

  • Connect
  • Dispatch on mount (data bootstrapping)
  • Dispatch on interval
  • Pull down to refresh
  • Show modal
  • Show action sheet
  • Placeholder
  • Orientation lock
  • Infinite scrolling
  • Error boundary

3: Composition Layouts

 

compose(
    connect(mapConversationList),

    data({
        onMount: apiActions.reqConversations,
    }),

    pagination('conversations', {
        limit: 10,
        onLoadNextPage: apiActions.reqConversations,
    }),
)

Behavior

(props: ConnectProps & PaginationProps) => (
    <FlexView>
        <SwipeableList
            data={props.conversations}
            onSwipe={props.onSwipe}
            renderItem={renderItem}
            onEndReached={props.onLoadNextPage}
        />
        <FloatingButton 
            onPress={props.onCreateMessagePress} />
    </FlexView>
)

Presentation

Code: Layout

TERRIBLE

navigation is

React Native Navigation

React Navigation

Navigator experimental

React Router Flux

Navigator

Navigator iOS

WALL

/app
    /ui
        /presentational
        /layouts
        /navigation
        

import ... from 'react-native-navigation'

Higher Order Components

+
Redux Middleware

Offline First

Redux Saga

Powerful

Expressive

Awesome

Mobile

"It's too complicated"

Code: API module

Diagram

Modules named by side effect source

Saga dedicated actions

Saga Modules

Code: Saga Root

"Regular" Redux Saga Tests
Are Flawed

 

 

use
Redux Saga Test Plan

Mental

Model

Overall Diagram

import saga, { sagaMiddleware } from 'app/saga'
import makeStore from 'app/store'
import ui from 'app/ui'

const store = makeStore(sagaMiddleware)

sagaMiddleware.run(saga)

const app = ui(store)

export default app

Time to Ship

"It doesn't work"

– User

RCTFatalException: 
Unhandled JS Exception:
TypeError: undefined is 
not an object (evaluating 'e.id') 

This error is located at: 
in t in t in t in t in 
Unknown in n in Connect(n)

Debugging

Name your components

export default function withFoo(Component) {
  function WithFoo(props) {
    // TODO: do something special here.
    return <Component {...this.props} />;
  }

  WithFoo.displayName = `withFoo(${Component.displayName || Component.name}`;
  return WithFoo;
}

Source: https://github.com/airbnb/javascript/issues/968

Use React Native Debugger

 

But....

  • Saga stack traces source the same (wrong) line
  • Hot module reloading doesn't work

Debugging API calls in Chrome

Stack traces are often in native code

Ship Again!

"It's slow"

– Android User

React Native Performance

Android First

Optimization

  • Reduce re-renders
    1. React.PureComponent
    2. shouldComponentUpdate()
    3. Getter cache (reselect)
    4. Remove arrow functions in render, notably in lists
    5. Control re-render (redux-batched-subscribe)
  • Native Navigation
  • Reduce time to component mount

Otherwise...

 

🤞

Scaling

Login

Hybrid

React Native

Experimentation Framework

Production Monitoring

MS App Center

  • Releases, builds, tests, crashes, events
  • Works with Code Push
  • JavaScript stack traces in crash logs
  • Nice to use, but events still suck

"How much native code did you write?"

0

*

"How much code is reused between iOS and Android?"

+95%

so?

Native Mobile App Challenges RN Ecosystem Solutions
UI React Native
Business Logic & Side Effects Redux Saga
State Management Redux
Tests Jest, Calabash, Detox
Types Jest & TypeScript
Code Quality Eslint, Prettier
Debugging React Native Debugger
Continuous Delivery & Deployments Code Push, Fastlane, Jenkins
Performance optimization Android-first
Production Monitoring MS App Center
Scaling 😜
Library Usage Do you care?
prettier developer efficiency ⭐️⭐️⭐️⭐️
eslint stability ⭐️⭐️⭐️⭐️
redux state management, action-based infra ⭐️⭐️⭐️
flow stability ⭐️⭐️⭐️
redux-saga side effects/bus. logic ⭐️⭐️
reselect, batched subscribe render cost reduction ⭐️⭐️
fastlane sane builds ⭐️
code push continuous delivery ⭐️
app center crash analytics ⭐️
calabash, detox acceptance tests ⭐️

React Native

⭐️ ⭐️ ⭐️ ⭐️ ⭐️

Thank you, team

Thank you, audience

Adam Terlson

@adamterlson (twitter, github)

slides.com/adamterlson

React Native: How we do it

By Adam Terlson

React Native: How we do it

  • 1,774