Pieter De Baets - An introduction to React Native
-
Upload
tlv-ios-dev -
Category
Mobile
-
view
135 -
download
2
Transcript of Pieter De Baets - An introduction to React Native
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