pieter de baets - an introduction to react native

Post on 19-Aug-2015

147 Views

Category:

Mobile

2 Downloads

Preview:

Click to see full reader

TRANSCRIPT

REACT NATIVE

NATIVE DEVELOPMENT SUCKS

SLOW DEVELOPMENT CYCLESLOW DEPLOYMENT CYCLE

DIFFERENT APIS TO CODE SAME THINGSSEPARATE PLATFORM TEAMS

X-PLATFORM TOOLS SUCK

POOR PERFORMANCENON-NATIVE FEEL

LIMITED FEATURE SUPPORTHARD TO DEBUG / POOR TOOLING

COMMON TO BOTHSTATEFUL, MUTABLE UI

HARD TO MANAGE COMPLEXITYHARD TO VERIFY CORRECTNESS

HARD TO MAINTAIN/RE-USE

ENTER: REACT NATIVE

SLOW DEVELOPMENT CYCLEINSTANT RELOAD

SLOW DEPLOYMENT CYCLEDOWNLOAD UPDATES OTA WITHOUT

RESUBMISSION**APPLE SAYS IT'S OK

DIFFERENT APIS TO CODE SAME THINGSCONSISTENT TOOLING & COMMON APIS FOR

EVERY PLATFORM

SEPARATE PLATFORM TEAMSSHARED SKILLSET*

*UP TO 90% CODE RE-USE BETWEEN IOS AND ANDROID

POOR PERFORMANCEVIRTUAL DOM

NON-NATIVE FEELBACKED BY STANDARD NATIVE VIEWS

LIMITED FEATURE SUPPORTEXTENSIBLE PLUGIN-BASED ARCHITECTURE

HARD TO DEBUG / POOR TOOLINGBREAKPOINTS AND SINGLE-STEP DEBUGGING

WITH BROWSER-BASED DEV TOOLS

STATEFUL, MUTABLE UIIMMUTABLE VIEWS, PURE RENDER FUNCTIONS, ONE-WAY DATA FLOW

REACT NATIVE ISFUNCTIONAL UIVIRTUAL DOM

FLEXBOX LAYOUTJAVASCRIPT FRONT-END

NATIVE BACK-END

FUNCTIONAL UIDEFINING STATES, NOT TRANSITIONS

IMPERATIVE UI: DEFINE TRANSITIONSFUNCTIONAL UI: DEFINE STATES

IMPERATIVE UI: DEFINE TRANSITIONS

IMPERATIVE UI: DEFINE TRANSITIONS

IMPERATIVE UI: DEFINE TRANSITIONS

IMPERATIVE UI: DEFINE TRANSITIONS

IMPERATIVE UI: DEFINE TRANSITIONS

3 STATES

9 TRANSITIONS

O(N2)

if (count > 99) { // branch 1 if (!hasFire()) { // branch 2 addFire(); }} else { if (hasFire()) { // branch 3 removeFire(); }}if (count === 0) { // branch 4 if (hasBadge()) { // branch 5 removeBadge(); } return;}if (!hasBadge()) { // branch 6 addBadge();}var countText = count > 99 ? '99+' : count.toString(); // branch 7getBadge().setText(countText);

FUNCTIONAL UI: DEFINE STATES

if (count === 0) { // state 1 return <Bell/>;}

FUNCTIONAL UI: DEFINE STATES

if (count === 0) { // state 1 return <Bell/>;} else if(count <= 99) { // state 2 return ( <Bell> <Badge count={count} /> </Bell> );}

FUNCTIONAL UI: DEFINE STATES

if (count === 0) { // state 1 return <Bell/>;} else if(count <= 99) { // state 2 return ( <Bell> <Badge count={count} /> </Bell> );} else { // state 3 return ( <Bell style={styles.onFire}> <Badge count=“99+” /> </Bell> );}

VIEW IS RECREATED EACH TIMEBUT ISN'T THAT SLOW?

VIRTUAL DOM**DOCUMENT OBJECT MODEL

(AKA VIEW HIERARCHY)

REACT CALCULATES CHANGESET ON THE JS SIDE

AUTORESIZING VS AUTOLAYOUT

AUTORESIZING VS AUTOLAYOUTFLEXBOX LAYOUT

FLEXBOX APIEXPLICT AND INFERRED POSITIONING

CONTAINERS SPECIFY LAYOUT FOR CHILDRENDIVIDES CONTENT INTO ROWS & COLUMNS

BASED ON CSS STANDARDS

FLEX DIRECTION

flexDirection: 'row' | 'column' (default)

This defines the main axis along which subviews are stacked.

JUSTIFY CONTENT

justifyContent: 'flex-start' (default) | 'center' | 'flex-end' | 'space-between' | 'space-around'

This defines how views align along the main axis.

ALIGN ITEMS

alignItems: 'flex-start' (default) | 'center' | 'flex-end'

This defines how views align along the cross axis.

FLEX

flex: (number)

A flex value > 0 indicates that a view should scale to fill its container. Flex values > 1 control the relative size of views inside their parent.

FLEX WRAP

flexWrap: 'nowrap' (default) | 'wrap'

With the flexWrap style, subviews that overflow the bounds of their container can be set to automatically wrap onto the next line.

JAVASCRIPT FRONT-END

JAVASCRIPT!?

WHY JAVASCRIPT?ALREADY SUPPORTED X-PLATFORM

REACT ALREADY USES ITONLY OPTION FOR OTA UPDATES

3.3.2 An Application may not download or install executable code.Interpreted code may only be used in an Application if all scripts,

code and interpreters are packaged in the Application and notdownloaded. The only exception to the foregoing is scripts andcode downloaded and run by Apple's built-in WebKit framework,provided that such scripts and code do not change the primary

purpose of the Application by providing features or functionalitythat are inconsistent with the intended and advertised purpose of

the Application as submitted to the App Store.

BUT... JAVASCRIPT SUCKS

WEAKLY TYPEDGARBAGE COLLECTED

INTERPRETED

IF ONLY JAVASCRIPT

function foo(fn, x) { return fn(x);}

IF ONLY JAVASCRIPT

function foo(fn, x) { return fn(x);}

WAS MORE LIKE SWIFT

function foo<X, Y>(fn: F<X, Y>, x: X): Y { return fn(x);}

FLOWSTATIC ANALYSIS, STRONG TYPING AND

TYPE INFERENCE FOR JAVASCRIPT

TYPE ALIASES TRUE CLASSES MAYBE TYPES UNIONS ENUMS GENERICS TUPLES MIXINS

ARROW FUNCTIONS DESTRUCTURING

GARBAGE COLLECTIONINTERPRETED CODE

GARBAGE COLLECTIONINTERPRETED CODE

ASYNC EXECUTION**JS RUNS ON A DEDICATED THREAD - DOESN'T BLOCK UI

EXAMPLEvar React = require('react-native');var { AppRegistry, StyleSheet, Text, View } = React;

class HelloWorld extends React.Component { render() { return ( React.createElement(View, {style: styles.container}, React.createElement(Text, null, "Hello World!") ) ); },});

var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }});

AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

JSX (JAVASCRIPT + XML)var React = require('react-native');var { AppRegistry, StyleSheet, Text, View } = React;

class HelloWorld extends React.Component { render() { return ( <View style={styles.container}> <Text>Hello World</Text> </View> ); },});

var styles = StyleSheet.create({ container: { flex: 1, justifyContent: 'center', alignItems: 'center', }});

AppRegistry.registerComponent('HelloWorld', () => HelloWorld);

NATIVE BACK-END

INTEGRATIONNSURL *src = [NSURL URLWithString:@"http://localhost:8081/index.ios.bundle"];NSString *moduleName = @"MyApp";

// Option 1

RCTRootView *rootView = [[RCTRootView alloc] initWithBundleURL:src moduleName:moduleName launchOptions:nil];

// Option 2

RCTBridge *bridge = [[RCTBridge alloc] initWithBundleURL:src moduleProvider:nil launchOptions:nil];

RCTRootView *rootView = [[RCTRootView alloc] initWithBridge:bridge moduleName:moduleName];

PLUGIN ARCHITECTUREBRIDGE MODULESVIEW MANAGERS

(MORE IN FUTURE)

BRIDGE MODULES// CalendarManager.m

@interface CalendarManager : NSObject <RCTBridgeModule>@end

@implementation CalendarManager

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(addEvent:(NSString *)name location:(NSString *)location){ RCTLogInfo(@"Pretending to create an event %@ at %@", name, location);}

@end

// CalendarManager.js

var CalendarManager = require('NativeModules').CalendarManager;CalendarManager.addEvent('Birthday Party', '4 Privet Drive, Surrey');

TYPE CONVERSION// CalendarManager.m

@implementation CalendarManager

RCT_EXPORT_MODULE()

RCT_EXPORT_METHOD(addEvent:(NSString *)name date:(NSDate *)date){ RCTLogInfo(@"Pretending to create an event %@ on %@", name, date);}

@end

// CalendarManager.js

CalendarManager.addEvent( 'Birthday Party', date.toTime() // returns time in seconds since unix epoch);

CALLBACKS// CalendarManager.m

@implementation CalendarManager...

RCT_EXPORT_METHOD(findEvents:(RCTResponseSenderBlock)callback){ NSArray *events = ... callback(@[[NSNull null], events]);}

@end

// CalendarManager.js

CalendarManager.findEvents((error, events) => { if (error) { console.error(error); } else { // do something with events }})

VIEW MANAGERS// MapManager.m

@interface MapManager : RCTViewManager <MKMapViewDelegate>@end

@implementation MapManager

RCT_EXPORT_MODULE()

- (UIView *)view{ return [[MKMapView alloc] init];}

@end

// MapView.js

var React = require('react-native');var { requireNativeComponent } = React;module.exports = requireNativeComponent('Map', null);

PROPERTY BINDING// MapManager.m

@implementation MapManager...RCT_EXPORT_VIEW_PROPERTY(pitchEnabled, BOOL)

@end

// MapView.js

var React = require('react-native');var { requireNativeComponent } = React;

class MapView extends React.Component { render() { return <Map {...this.props} />; }}

MapView.propTypes = { pitchEnabled: React.PropTypes.bool,};

var Map = requireNativeComponent('Map', MapView);module.exports = MapView;

EVENTS// MapManager.m

@implementation MapManager

RCT_EXPORT_MODULE()

- (UIView *)view{ MKMapView *map = [[MKMapView alloc] init]; map.delegate = self; return map;}

#pragma mark MKMapViewDelegate

- (void)mapView:(MKMapView *)mapView regionDidChangeAnimated:(BOOL)animated{ MKCoordinateRegion region = mapView.region; NSDictionary *event = @{ @"lat": @(region.center.latitude), @"long": @(region.center.longitude) }; [self.bridge.eventDispatcher sendInputEventWithName:@"topChange" body:event];}

@end

EVENTS// MapView.js

class MapView extends React.Component { constructor() { this._onChange = this._onChange.bind(this); }

_onChange(event: Event) { if (!this.props.onRegionChange) { return; } this.props.onRegionChange(event.nativeEvent.region); }

render() { return <Map {...this.props} onChange={this._onChange} />; }}

MapView.propTypes = { pitchEnabled: React.PropTypes.bool, onRegionChange: React.PropTypes.func,};

WHAT ABOUT SWIFT?// CalendarManagerBridge.m

#import "RCTBridgeModule.h"

@interface RCT_EXTERN_MODULE(CalendarManager, NSObject)

RCT_EXTERN_METHOD(addEvent:(NSString *)name location:(NSString *)location date:(NSDate *)date)

@end

// CalendarManager.swift

@objc(CalendarManager)class CalendarManager: NSObject {

@objc func addEvent(name: String, location: String, date: NSDate) -> Void { // Date is ready to use! }}

LINKS

React Native on Githubhttps://facebook.github.io/react-native/

Colin Eberhardt's RayWenderlich.com Tutorialhttp://tinyurl.com/rw-react-tutorial

Christopher Chedeau’s ReactJS Conf Keynotehttps://www.youtube.com/watch?v=7rDsRXj9-cU

Q&A

top related