React Native for Better Apps

It (mostly) just works!

Gian Marco Toso

@gianmarcotoso

gianmarcotoso

gianmarcotoso.com

polarityb.it

Software Engineer, The One Who Knocks

🎩 About Me 🎩

Software Engineer from Turin, Italy

MSc in Computer Engineering

Researcher and Freelancer

🍔 About My Stack 🍔

Docker

PHP/Laravel

"It was working on my machine!"

The monolith is here to stay

NodeJS/TypeScript

Seriously, it's awesome!

React + Redux

The Flux Capacitor!

(formerly) iOS/ObjectiveC

and I still get nightmares

Mobile Applications

Very popular, with a huge market

All the cool kids are developing them 😎

Native Applications

A nightmare to develop

A lot of devices

A lot of resolutions

Different operating systems with different rules

Different programming languages

Hybrid Applications

Write Once, Run Anywhere!

One language (JavaScript)

One runtime environment (the WebView)

Less than ideal UI performance

React Native

UI written with ReactJS

Modern JavaScript Code

Native extensions are bridged to JavaScript

 

UI Components are mapped to native components

Apps written with React Native are real native apps!

An Example

import React from 'react'
import { View, Text, AppRegistry } from 'react-native'

class HelloWorld extends React.Component {
    render() {
        return (
            <View style={{
                flex: 1, 
                justifyContent: 'center', 
                alignItems: 'center'
            }}>
                <Text style={{fontSize: 72}}>Hello, World!</Text>
            </View>
        )
    }
}

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

CLI Tool

React Native comes with a command line utility that can be installed with `yarn install react-native-cli`

The CLI tool can be used to create new projects (`react-native init`) or to run them (`react-native run-*`)

In order to run a React Native project on a specific platform, you'll need to have the proper SDK installed!

Styling

Inline Styles

Flexbox Algorithm

CSS-Like syntax

Styles can be defined as POJOs

Styles should be defined using the StyleSheet class

An Example

import React from 'react'
import { View, Text, StyleSheet } from 'react-native'

const styles = StyleSheet.create({
    container: {
        flex: 1, 
        justifyContent: 'center', 
        alignItems: 'center'
    },
    titleText: {   
        fontSize: 72
    },
    red: {
        color: 'red'
    }
})

const HelloWorld = () => (
    <View style={styles.container}>
        <Text style={[styles.titleText, styles.red]}>Hello, World!</Text>
    </View>
)

export default HelloWorld

Built-in Components

React Native has many built-in component to handle common operations

Most components are compatible with both supported platforms

TouchableHighlight

The TouchableHighlight component allows anything to be able to handle touch events

Can be used to wrap most components

Exposes an `onPress` method that receives an Event Handler

TouchableHighlight

import React from 'react'
import { TouchableHighlight, View, Image, Text } from 'react-native'

class TouchableCard extends React.Component {
    handleCardTouch = () => {
        this.props.onCardTouched()
    }

    render() {
        return (
            <View>    
                <TouchableHighlight onPress={this.handleCardTouch}>
                    <View>
                        <Image source={require('image.png')} />
                        <Text>Some Fancy Image</Text>
                    </View>
                </TouchableHighlight>
            </View>
        )
    }
}

export default TouchableCard

Modals

The Modal component can wrap a View and all its content and present it modally whenever required

A Modal will become visible as soon as its visible prop becomes true, and will disappear when it becomes false

It's possible to change the animation used to show the modal by setting the animationType to either fade, slide or none

Modals

import React from 'react'
import { View, Modal, Text, Button } from 'react-native'

class ModalContainer extends React.Component {
    state = { isModalVisible: false }

    render() {
        <View>
            <Modal visible={this.state.isModalVisible}>
                <View>
                    <Text>Hello, Modal!</Text>
                    <Button 
                        title="Bye" 
                        onPress={() => this.setState({isModalVisible: false})} 
                    />
                </View>
            </Modal>

            <Button 
                title="Open Modal" 
                onPress={() => this.setState({isModalVisible: true})} 
            />
        </View>
    }
}

StatusBar

The StatusBar component allows to configure the behavior of the mobile OS' status bar

It can be included in any screen in order to have different styles for different pages

StatusBar

import React from 'react'
import { StatusBar, View } from 'react-native'

class MyFancyPage extends React.Component {
    render() {
        return (
            <View>
                <StatusBar barStyle="light-content" />
                <View>
                    {/* Page content... */}
                </View>
            <View>
        )
    }
}

ListView

The ListView component allows to create scrollable views containing items of the same type

The component uses a DataSource to populate itself on every render

It also uses a callback to decide how to render each row, and it's highly configurable

ListView

class MyComponent extends Component {
  constructor() {
    super()

    const ds = new ListView.DataSource({rowHasChanged: (r1, r2) => r1 !== r2})

    this.state = {
      dataSource: ds.cloneWithRows(['row 1', 'row 2'])
    };
  }

  render() {
    return (
      <ListView
        dataSource={this.state.dataSource}
        renderRow={(rowData) => <Text>{rowData}</Text>}
      />
    );
  }
}

Navigation

The Navigator Component allows to handle navigation 

Navigation is handled through the use or routes and a navigation stack.

When a route is matched, a specific component is returned.

Navigation is handled through the use or routes and a navigation stack.

Navigation

const routes = [
    { key: 'home', title: 'Home Screen', component: (p) => <Home {...p} /> },
    { key: 'login', title: 'Login', component: (p) => <Login {...p} /> },
]

const Home = ({navigator}) => (
    <Button onPress={() => navigator.push('login')} title="Login" />
)

const renderRoute = (route, navigator) => (
    routes.find(r => r === route.key)
        .component({navigator})
)

const NavigationStack = () => (
    <Navigator
        initialRoute={routes[0]}
        renderScene={renderRoute}
    />
)

export default NavigationStack

NavigationBar

The Navigator.NavigationBar allows to create a Navigation Bar, persistent  across scenes

It allows to define the Left and Right buttons behavior, as well as the Title

NavigationBar

import React from 'react'
import { Navigator, Text, Button } from 'react-native'

const LeftButton = (route, navigator, index, navState) => (
    <Button title="Back" onPress={() => navigator.pop()} />
)

const RightButton = (route, navigator, index, navState) => {}

const Title = (route, navigator, index, navState) => (
    <Text>{route.title}</Text>
)

const Navbar = () => (
    <Navigator.NavigationBar
        routeMapper={{
            LeftButton,
            RightButton,
            Title
        }}
    />
)

// Passed to the `navigationBar` prop of the `Navigator`
export default Navbar

Navigation

The Navigator Component is less than ideal

Configuring navigation this way is hard, cumbersome and error prone

You need a way to pass the `navigator` object around if you want to do anything useful

The Navigator component is being deprecated, along with its newer alternative NavigatorExperimental

The official recommended replacement is a third party library called React Navigation

Built-in Modules

React Native has many built-in modules to handle operations common to mobile devices

Most modules are compatible with both supported platforms

Module actions are called imperatively

Vibration

The Vibration module makes the phone vibrate using the specified pattern.

The pattern can be repeated any number of times

import { Vibration } from 'react-native'

Vibration.vibrate(
    [0, 100],      // pattern
    1              // repeat
)

Alert

The Alert module shows a popup dialog box with confirmation buttons

Each button can have an `onPress` event handler

import { Alert } from 'react-native'

Alert.alert(
    'Confirm?'
    'Do you confirm this action?',
    [
        { title: 'Yes', onPress: () => {} },
        { title: 'No', onPress: () => {}, style: 'cancel' },
    ]
)

AppState

The AppState module tells us if the app is in the background, foreground or inactive state.

It can be listened to by using the addEventListener method

import { AppState } from 'react-native'

let state = 'background'
AppState.addEventListener('change', (newState) => {
    state = newState
})

Dimensions

Sometimes it can be useful to know the dimensions of either the screen or the window.

The Dimensions module comes to our aid, by allowing to read dimensions at any time

import { Dimensions } from 'react-native'

const { height, width } = Dimensions.get('window')

const myStyle = {
    height, width
}

const myComponent = ({children}) => <View style={myStyle}>{children}</View>

// This will soon be possible (not yet!):
Dimensions.addEventListener('change', ({window, screen}) => {
    console.log('new dimensions:', window, screen)
})

JavaScript Ecosystem

A React Native app's code is written in JavaScript

This means you can use most of the libraries you're used to!

Redux

Axios

Fetch

LocalForage

Lodash

RxJS

Third Party Components

You'll find many third-party libraries and components online

Some are even recommended by the React Native core team, like AirBnB's `react-native-maps`

Animations

Animations are a big part of a Mobile Application's UX

React Native makes animating components somewhat easy with the Animated module

Animations

The Animated module exposes replacement components View, Image and Text

These components' style prop can receive instances of the Animated.Value class instead of regular numbers

Animation functions such as timing or spring can be used to animate Animated.Values smoothly

Animations

import React from 'react'
import { Text, Animated, Easing } from 'react-native'

const _opacity = new Animated.Value(1)

const changeOpacity = (value = 0) => {
    Animated.timing({
        toValue: value,
        delay: 1000,
        easing: Easing.inOut(Easing.ease)
    }).start( () => changeOpacity((value + 1) % 2) )
}    

class Wobble extends React.Component {
    componentDidMount = () => changeOpacity()
    render() {
        return (
            <Animated.View style={{opacity: _opacity}}>
                <Text>Wobble!</Text>
            </Animated.View>
        )
    }
}

export default Wobble

React Native Web

Third-party library to render React Native apps inside a web browser

Maps React Native's primitives to standard HTML tags

Allows to debug an App in the browser where possible - there is no support for mobile-specific functionalities!

Not really production ready

React Native Windows

Third party module that allows to deploy React Native apps on the Universal Windows Platform

Maps React Native components to native C# primitives

Still under development, YMMV

The End

Thank you!

@gianmarcotoso

Better Apps with React Native

By Gian Marco Toso

Better Apps with React Native

Slides for my talk at the 2017 FEVR Meetup in Verona

  • 1,474