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

Living in an async world of React Native

Living in an async world of React Native

React Native’s asynchronous model of bridging between UI code written in JS and the UI thread that runs most of the native code has many advantages to the app users. But in same cases it may become a developer’s nightmare to deal with, especially when it comes to complex touch interactions, animations or "that damned AsyncStorage". In my talk I’ll be giving some tips on how to not get lost in the asynchronous world of React Native app development. We will discuss some common mistakes and important app and library design principles that are imposed by the asynchronous nature of the bridge. At the end of the presentation we give a short into to react-native-gesture-handler library.

Krzysztof Magiera

October 27, 2016
Tweet

More Decks by Krzysztof Magiera

Other Decks in Programming

Transcript

  1. ASYNC STORAGE JS Thread Native Thread getItem('onboardingCompleted'); AsyncStorage if (YES)

    { return <MainApp/> } else { return <Onboarding/> } UI Thread
  2. ASYNC STORAGE JS Thread Native Thread getItem('onboardingCompleted'); AsyncStorage if (YES)

    { return <MainApp/> } else { return <Onboarding/> } UI Thread layout render render rendering !
  3. CREATING “SYNC” STORAGE - (NSDictionary<NSString *, id> *)constantsToExport RCTBridgeModule iOS

    public @Nullable Map<String, Object> getConstants() BaseJavaModule Android const MySyncStorageModule = require('NativeModules').MySyncStorageModule; const _data = MySyncStorageModule.data; function getItem(key) { return _data[key]; } function setItem(key, value) { _data[key] = value; MySyncStorageModule.asyncSet(key, value); } JS
  4. SCREEN ORIENTATION https://www.npmjs.com/package/react-native-orientation Orientation.getInitialOrientation(); // PORTRAIT, LANDSCAPE, etc... Orientation.addOrientationListener((orientation) =>

    { // orientation = PORTRAIT, LANDSCAPE, etc... }); DeviceEventEmitter.addListener('namedOrientationDidChange', (orientation) => { // orientation.rotationDegrees // orientation.isLandscape }); React Native Core (android only)
  5. SCREEN ORIENTATION JS Thread Native Thread UI Thread orientation changes

    if (isLandscape) { return <LandscapeView/> } else { return <PortraitView/> } event Layout rendering
  6. SCREEN ORIENTATION JS Thread Native Thread UI Thread orientation changes

    if (isLandscape) { return <LandscapeView/> } else { return <PortraitView/> } event Layout rendering render Layout rendering !
  7. CONTROL ORIENTATION CHANGES IN JS onOrientationChange = (orientation) => {

    lockToOrientation(orientation); this.setState({ orientation }) }; lockToOrientation(initialOrientation); render() { if (this.state.orientation === 'LANDSCAPE') { return <LandscapeView />; } else { return <PortraitView /> } }
  8. ANIMATED var someValue = new Animated.Value(0); // can be kept

    in the component state return ( <Animated.View style={{opacity: someValue}}> <Text>Hello!</Text> </Animated.View> ); Animated.spring( someValue, { toValue: 1, friction: 0.5, } ).start();
  9. ANIMATED JS Thread Native Thread Animated.spring(...).start() UI Thread event animationLoop()

    rendering update some views UIManager setTimeout TimerModule
  10. ANIMATED JS Thread Native Thread Animated.spring(...).start() UI Thread event animationLoop()

    rendering update some views UIManager setTimeout TimerModule event TimerModule animationLoop() rendering update some views UIManager event TimerModule animationLoop() update some views UIManager
  11. TOUCH EVENTS JS Thread Native Thread UI Thread rendering update

    some views UIManager handleTouch() event
  12. handleTouch() f1 f2 f3 … sorry man, have to compose

    the frame input compose VSYNC input compose input rendering UI Thread JS Thread TOUCH EVENTS
  13. GESTURE HANDLING JS Thread Native Thread UI Thread handleTouch() send

    touch down event Touch DOWN Scroll recognizer Touchable
  14. GESTURE HANDLING JS Thread Native Thread UI Thread handleTouch() send

    touch down event Touch DOWN Touch MOVE handleTouch() handleTouch() Touch MOVE Touch MOVE send touch move event send touch move event Scroll recognizer Touchable
  15. GESTURE HANDLING JS Thread Native Thread UI Thread handleTouch() send

    touch down event Touch DOWN Touch MOVE handleTouch() handleTouch() Touch MOVE Touch MOVE send touch move event send touch move event send scroll event handleScroll() Scroll recognizer " Touchable #
  16. GESTURE HANDLING JS Thread Native Thread UI Thread Touch DOWN

    Scroll recognizer handleTouch() send touch down event Touchable
  17. GESTURE HANDLING JS Thread Native Thread UI Thread Touch DOWN

    handleTouch() handleTouch() Touch MOVE Touch MOVE Touch UP send touch move event send touch move event Scroll recognizer handleTouch() send touch down event Touchable
  18. GESTURE HANDLING JS Thread Native Thread UI Thread Touch DOWN

    handleTouch() handleTouch() Touch MOVE Touch MOVE Touch UP send touch move event send touch move event Scroll recognizer handleTouch() send touch up event # handleTouch() send touch down event Touchable "
  19. JS Thread Native Thread UI Thread Touch DOWN handleTouch() send

    touch down event PanResponder GESTURE HANDLING Scroll recognizer
  20. JS Thread Native Thread UI Thread Touch DOWN Touch MOVE

    handleTouch() handleTouch() Touch MOVE Touch MOVE send touch move event send touch move event handleTouch() send touch down event PanResponder GESTURE HANDLING Scroll recognizer
  21. JS Thread Native Thread UI Thread Touch DOWN Touch MOVE

    handleTouch() handleTouch() Touch MOVE Touch MOVE send touch move event send touch move event handleTouch() send touch down event PanResponder " GESTURE HANDLING Scroll recognizer "
  22. GESTURE HANDLING ! ! ! ! ! ! ! !

    ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! ! !
  23. class Button extends React.Component { render() { return ( <TapGestureHandler>

    <View style={[this.props.style, this.state.pressed && styles.pressed]}> <Text>Hello</Text> </View> </TapGestureHandler> ); } } GESTURE HANDLING
  24. class Button extends React.Component { render() { return ( <TapGestureHandler

    onHandlerStateChange={this._onStateChange}> <View style={[this.props.style, this.state.pressed && styles.pressed]}> <Text>Hello</Text> </View> </TapGestureHandler> ); } _onStateChange = (event) => { } } GESTURE HANDLING
  25. class Button extends React.Component { render() { return ( <TapGestureHandler

    onHandlerStateChange={this._onStateChange}> <View style={[this.props.style, this.state.pressed && styles.pressed]}> <Text>Hello</Text> </View> </TapGestureHandler> ); } _onStateChange = (event) => { this.setState({ pressed: event.state === State.BEGAN }); if (event.state === State.ACTIVE) { this.props.onClick(); } } } GESTURE HANDLING
  26. class Draggable extends React.Component { constructor(props) { super(props); this._translateX =

    new Animated.Value(0); this._translateY = new Animated.Value(0); } render() { return ( <Animated.View style={[styles.box, { transform: [ {translateX: this._translateX}, {translateY: this._translateY} ]}]}/> ); } } GESTURE HANDLING
  27. class Draggable extends React.Component { constructor(props) { super(props); this._translateX =

    new Animated.Value(0); this._translateY = new Animated.Value(0); } render() { return ( <PanGestureHandler> <Animated.View style={[styles.box, { transform: [ {translateX: this._translateX}, {translateY: this._translateY} ]}]}/> </PanGestureHandler> ); } } GESTURE HANDLING
  28. GESTURE HANDLING class Draggable extends React.Component { constructor(props) { super(props);

    this._translateX = new Animated.Value(0); this._translateY = new Animated.Value(0); } render() { return ( <PanGestureHandler onGestureEvent={this._onGestureEvent} onHandlerStateChange={this._onHandlerStateChange}> <Animated.View style={[styles.box, { transform: [ {translateX: this._translateX}, {translateY: this._translateY} ]}]}/> </PanGestureHandler> ); } }
  29. _onGestureEvent = (event) => { this._translateX.setValue(event.nativeEvent.translationX); this._translateY.setValue(event.nativeEvent.translationY); } _onHandlerStateChange =

    (event) => { if (event.nativeEvent.oldState === State.ACTIVE) { this._lastOffset.x += event.nativeEvent.lastTranslationX; this._lastOffset.y += event.nativeEvent.lastTranslationY; this._translateX.setOffset(this._lastOffset.x); this._translateX.setValue(0); this._translateY.setOffset(this._lastOffset.y); this._translateY.setValue(0); } } GESTURE HANDLING
  30. _onHandlerStateChange = (event) => { if (event.nativeEvent.oldState === State.ACTIVE) {

    this._lastOffset.x += event.nativeEvent.lastTranslationX; this._lastOffset.y += event.nativeEvent.lastTranslationY; this._translateX.setOffset(this._lastOffset.x); this._translateX.setValue(0); this._translateY.setOffset(this._lastOffset.y); this._translateY.setValue(0); } } GESTURE HANDLING _onGestureEvent = Animated.event( [{ nativeEvent: { translationX: this._translateX, translationY: this._translateY }}], { useNativeDriver: true } );
  31. GESTURE HANDLING render() { return ( <LongPressGestureHandler minDurationMs={2000}> <TapGestureHandler shouldCancelWhenOutside={true}>

    <PanGestureHandler minDeltaX={50}> <Animated.View style={...}/> </PanGestureHandler> </TapGestureHandler> </LongPressGestureHandler> ); } https://github.com/kmagiera/react-native-gesture-handler