React Native Workshop - React Alicante

125
September 2017 Introduction to React Native Nacho Martín

Transcript of React Native Workshop - React Alicante

September 2017

Introduction to

React NativeNacho Martín

Nacho Martin

I write code at Limenius.

We build tailor-made projects, and provide consultancy and formation.

We are very happy with React and React Native.

Roadmap:What is React Native Starting a project Working with Styles Layout Lists Navigation Networking Touching the native side

What is React Native?

Fundamental premise of React

Give me a state and a render() method that depends on it and forget about how and when to render.*

Fundamental premise of React

Give me a state and a render() method that depends on it and forget about how and when to render.*

* Unless you want more control.

render() { return ( <div className="App"> <button onClick={this.tick}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ) }

render() { return ( <div className="App"> <button onClick={this.tick}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ) }

render() { return ( <View style={…}> <Button onPress={this.tick}>Click me!</Button> <Text>Clicks: {this.state.count}</Text> </View> ) }

render() { return ( <div className="App"> <button onClick={this.tick}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ) }

render() { return ( <View style={…}> <Button onPress={this.tick}>Click me!</Button> <Text>Clicks: {this.state.count}</Text> </View> ) }

render() { return ( <div className="App"> <button onClick={this.tick}>Click me!</button> <span>Clicks: {this.state.count}</span> </div> ) }

render() { return ( <View style={…}> <Button onPress={this.tick}>Click me!</Button> <Text>Clicks: {this.state.count}</Text> </View> ) }

React Native

How is this possible

Reconciliation

Determines what parts of the tree have changed

How is this possible

Reconciliation

Determines what parts of the tree have changed

Rendering

Actually updates the app

How is this possible

Reconciliation

Determines what parts of the tree have changed

Rendering

Actually updates the app

We can have several renderers

React targetsMain

react-dom react-native

Web iOS android

React targetsMain

react-dom react-native

Web iOS android

But also

React targetsMain

react-dom react-native

Web iOS android

But also

react-art  react-canvas react-three ReactLibertyreact-worker-dom

React targetsMain

react-dom react-native

Web iOS android

But also

react-art  react-canvas react-three ReactLibertyreact-worker-dom

react-konsul raxreact-native  react-blessedreact-tvml

React targetsMain

react-dom react-native

Web iOS android

But also

react-art  react-canvas react-three ReactLibertyreact-worker-dom

react-konsul raxreact-native  react-blessedreact-tvml

React-GLreact-vr  react-hardware react-fs-renderer react-x11

React targetsMain

react-dom react-native

Web iOS android

But also

react-art  react-canvas react-three ReactLibertyreact-worker-dom

react-konsul raxreact-native  react-blessedreact-tvml

React-GLreact-vr  react-hardware react-fs-renderer react-x11

redocx react-titanium  React-Gibbon react-pdf  react-test-renderer

React targetsMain

react-dom react-native

Web iOS android

But also

react-art  react-canvas react-three ReactLibertyreact-worker-dom

react-konsul raxreact-native  react-blessedreact-tvml

React-GLreact-vr  react-hardware react-fs-renderer react-x11

redocx react-titanium  React-Gibbon react-pdf  react-test-renderer

ink  react-sketchapp 

React blessed

React blessed

React Sketch.app

React VR

Demo

How Native is React Native?

JS Thread

Business logic & Description of what

components to render

How Native is React Native?

JS Thread Main Thread

UI manipulation with native components

Business logic & Description of what

components to render

How Native is React Native?

JS Thread Main Thread

UI manipulation with native components

Business logic & Description of what

components to render Bridge

How much code can we reuse?

Tip: if you develop in one platform, try it in the other from time to time

70%? 80%? 90%?

How to start a project

Option 1: create-react-native-app

$ npm install -g create-react-native-app

$ create-react-native-app AwesomeProject

Option 1: create-react-native-app

Only JS, no iOS or Android code (outside node_modules)

If you want to modify native code, $ npm run eject

Uses the Expo app to test in real device

Meant to have a quick way of trying react-native

Option 2: react-native init

$ npm install -g react-native-cli

$ react-native init AwesomeProject

Option 2: react-native init

Complete project with native code

More control

Needed to use things like CodePush

Doesn’t need external tools to publish to the store

Getting our hands dirty

git clone https://github.com/Limenius/workshop-react-native.git

You are supposed to have done this

🙏

And then yarn install

git checkout dirtyhands

react-native run-ios

And then

Or

react-native run-android

git checkout dirtyhands

react-native run-ios

And then

Or

react-native run-android

Debug tools Cmd + d

(Cmd + r)

Play around

Open app/App.js with an editor

<Text style={styles.welcome}>Hi there!</Text>Change the text in

Try nesting <Text> <Text style={styles.welcome}>Hi there!</Text> Amigo</Text>

Try changing some styles

welcome: { fontSize: 100, textAlign: 'center', margin: 10,},

Try placing a console.log(‘hi’) before return(… and see it in Chrome dev tools

Familiarize with errorsWhat happens if we…

remove a closing tag (</View>)

<View style={styles.container}> Hi there!</View>

put text not wrapped in <Text/>

try to comment a JSX line with //return ( <View style={styles.container}> <Text style={styles.welcome}>Hi there!</Text> </View> <View/>)

have two root elements

use wrong properties for styles ( rename flex -> flexo )

remove the words export default

Basic components

git checkout elements

git reset HEAD --hard(To discard your changes)

Exercise: Play with the basic components

app/App.js

Exercise: Build new components

Can you build a new component combining others? Ideas: Image with footer (<Text/>), two buttons that display different alerts

Can you pass props to that component? Ideas: Pass the text of the footer with props, pass also the image, pass the titles of the buttons

Can your build a component with local state? Ideas: Modify the counter to have a “minus 1” button

Style 💅

No CSS. Everything is JS<View style={{ borderLeftColor: Colors.accent, borderLeftWidth: 9, backgroundColor: Colors.backgroundSection, padding: 18, paddingVertical: 9,}}>

No class No dimensions in pixels No things like padding: 19 9 3 1 camelCased

Use constants

StyleSheet

<View style={Styles.headline}>

const Styles = StyleSheet.create({ headline: { borderLeftColor: Colors.accent, borderLeftWidth: 9, backgroundColor: Colors.backgroundSection, paddingLeft: 18, paddingRight: 18, paddingTop: 9, paddingBottom: 9, },})

Combination

<View style={[Styles.headline, {backgroundColor: 'red'}]}>

const Styles = StyleSheet.create({ headline: { borderLeftColor: Colors.accent, borderLeftWidth: 9, paddingLeft: 18, paddingRight: 18, paddingTop: 9, paddingBottom: 9, },})

git checkout styles

(To discard your changes)

git reset HEAD --hard

We want to do this

Our first goal is to get this

(We will practice layouts in the next section)

components/MovieHeader.js

height: 210

fontWeight: ‘bold’fontSize: FontSizes.LargeTitle48x48, roundedbackgroundColor: Colors.highlightcolor: Colors.textcolor: Colors.textfontWeight: ‘bold’

border: left, size 9, Colors.accentpadding: 9, 18backgroundColor: Colors.backgroundSection

Exercise 1

84x84

Colors.text, bold

Container has: border at bottom size 1, Colors.subtleAccentand background is 'white'

components/ListItemActor.js

Exercise 2

FontSizes.giganticColors.background

Container has: a background with color: Colors.highlight

components/MainHeader.js

Image is 40x90

FontSizes.subheadWith weight ‘200’Colors.background

Pro exercise: Think how would you add support for themes

Dimensions

import { Dimensions,} from 'react-native'

const windowSize = Dimensions.get('window')

mainImage: { height: windowSize.height /3, width: undefined},

components/MovieHeader.js

Dimensions

import { Dimensions,} from 'react-native'

const windowSize = Dimensions.get('window')

mainImage: { height: windowSize.height /3, width: undefined},

Our image height depends on the Height of the window

Use sparingly

components/MovieHeader.js

Dimensions

import { Dimensions,} from 'react-native'

const windowSize = Dimensions.get('window')

mainImage: { height: windowSize.height /3, width: undefined},

Our image height depends on the Height of the window

Use sparingly

Exercise: Can you make another style dependant of Dimensions? What will happen if the device is rotated? Can you find anything in the documentation to fix it?

components/MovieHeader.js

Further reading

https://www.okgrow.com/posts/react-native-styling-tips

https://madebymany.com/stories/a-year-of-react-native-styling-part-1

https://medium.com/robin-powered/introducing-glamorous-for-react-native-1b8365e7f33f

https://github.com/styled-components/styled-components

Explore libraries to do CSS in JS

Layouts

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column' flexDirection: ‘row’(Default)

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column' flexDirection: ‘row’(Default)

main direction

cros

s dire

ctio

n

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column' flexDirection: ‘row’(Default)

main direction

cros

s dire

ctio

n

cross direction

mai

n di

rect

ion

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column’

(Default)

‘flex-start’ ‘flex-end’ ‘center' ‘space-around’ ‘space-between’

flexDirection: ‘row’

(Default)

‘flex-start’ ‘flex-end’ ‘center' ‘space-around’ ‘space-between’

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column’

(Default)

‘flex-start’ ‘flex-end’ ‘center' ‘stretch’ ‘baseline’

flexDirection: ‘row’

(Default)

‘flex-start’ ‘flex-end’ ‘center' ‘stretch’ ‘baseline’

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘row’

‘baseline’

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flexDirection: ‘column’

alignSelf: ’end’

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flex: 1

flex: 0flex: 0

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flex: 5

flex: 2

flex: 3

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flex: 0.5

flex: 0.2

flex: 0.3

This is flexbox realmflexDirection justifyContent alignItems alignSelf flex alignContent flexBasis flexGrow flexShrink flexWrap

flex: 25

flex: 10

flex: 15

git checkout layout

(To discard your changes)git reset HEAD --hard

Our goal

app/components/MovieHeader.js

app/components/MovieHeader.js

18

Exercise 1

app/components/ListItemActor.js

Exercise 2

Hint: create subviews if you need them

Optional: can you come up with a different layout for any of our three components?

app/components/MainHeader.js

Further reading

https://facebook.github.io/yoga/docs/getting-started/Docs:

https://github.com/jondot/ReactNativeKatasVery good practice:

Lists

Lists are important in mobile

Naive lists, as in the web

export default class Movie extends Component { constructor(props) { super(props) this.state = { movie: movies.find((movie) => movie.name === 'Pulp Fiction') } }

render() { return ( <View> <MovieHeader movie={this.state.movie}/> { this.state.movie.actors.map(actor => ( <ListItem key={actor} name={actor} image={actors[actor].image}/> ))} </View> ) }}

app/components/Movie.js

Naive lists, as in the web

export default class Movie extends Component { constructor(props) { super(props) this.state = { movie: movies.find((movie) => movie.name === 'Pulp Fiction') } }

render() { return ( <View> <MovieHeader movie={this.state.movie}/> { this.state.movie.actors.map(actor => ( <ListItem key={actor} name={actor} image={actors[actor].image}/> ))} </View> ) }}

Important to help the reconciler do its work

app/components/Movie.js

Exercise

git checkout lists

Can you build a list of movies in app/components/MovieList.js ?

(To discard your changes)git reset HEAD --hard

FlatList

Highly optimized List component

Features:

• Scroll loading (onEndReached).

• Pull to refresh (onRefresh / refreshing).

• Configurable viewability (VPV) callbacks (onViewableItemsChanged / viewabilityConfig).

• Horizontal mode (horizontal).

• Intelligent item and section separators.

• Multi-column support (numColumns)

• scrollToEnd, scrollToIndex, and scrollToItem

• Better Flow typing.

FlatList

render() { return ( <View> <MovieHeader movie={this.state.movie}/> <FlatList data={this.state.movie.actors} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> } /> </View> )}

app/components/Movie.js

FlatList

FlatList

What about the keys?

FlatList

What about the keys?

<FlatList data={this.state.movie.actors} keyExtractor={item => item} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> }/>

FlatList

What about the keys?

<FlatList data={this.state.movie.actors} keyExtractor={item => item} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> }/>

FlatList

Scrolleable area

<FlatList data={this.state.movie.actors} keyExtractor={item => item} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> }/>

<MovieHeader movie={this.state.movie}/>

FlatList

Desired scrolleable area

FlatList

Desired scrolleable area

<FlatList data={this.state.movie.actors} ListHeaderComponent={<MovieHeader movie={this.state.movie}/>} keyExtractor={item => item} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> }/>

FlatList

Desired scrolleable area

<FlatList data={this.state.movie.actors} ListHeaderComponent={<MovieHeader movie={this.state.movie}/>} keyExtractor={item => item} renderItem={({item}) => <ListItem name={item} image={this.state.actors[item].image}/> }/>

Exercise

Can you use FlatList in app/components/MovieList.js ?

git checkout flatLists

item => itemreminder

In this case works asfunction(item) { return item}

git reset HEAD —hard(To discard your changes)

Further reading

https://facebook.github.io/react-native/blog/2017/03/13/better-list-views.html

Read the docs of the components:

FlatList SectionList VirtualizedList

Navigation

Navigation

react-navigation

Several options

StackNavigator TabNavigator DrawerNavigator Or combine them

Several options

StackNavigator TabNavigator DrawerNavigator Or combine them

Several options

StackNavigator TabNavigator DrawerNavigator Or combine them

Several options

StackNavigator TabNavigator DrawerNavigator Or combine them

Let’s do it

git checkout navigation

(To discard your changes)

git reset HEAD --hard

Let’s do it

const App = StackNavigator({ Home: { screen: MovieList }, Movie: { screen: Movie },}, { navigationOptions: { headerTintColor: Colors.accent, headerStyle: Styles.header, }})

app/App.js

Let’s do it

const App = StackNavigator({ Home: { screen: MovieList }, Movie: { screen: Movie },}, { navigationOptions: { headerTintColor: Colors.accent, headerStyle: Styles.header, }})

app/App.jsexport default class MovieList extends Component { static navigationOptions = { title: 'Movies', } //…

app/components/MovieList.js

Let’s do it

const App = StackNavigator({ Home: { screen: MovieList }, Movie: { screen: Movie },}, { navigationOptions: { headerTintColor: Colors.accent, headerStyle: Styles.header, }})

app/App.jsexport default class MovieList extends Component { static navigationOptions = { title: 'Movies', } //…

app/components/MovieList.js

<FlatList data={this.state.movies} ListHeaderComponent={<MainHeader/>} keyExtractor={item => item.name} renderItem={({item}) => <TouchableHighlight underlayColor={Colors.subtleAccent} activeOpacity={0.5} onPress={() => navigate('Movie', {name: item.name})} > <View> <ListItem name={item.name} image={item.image}/> </View> </TouchableHighlight> }/>

In render()

Let’s do it

const App = StackNavigator({ Home: { screen: MovieList }, Movie: { screen: Movie },}, { navigationOptions: { headerTintColor: Colors.accent, headerStyle: Styles.header, }})

app/App.jsexport default class MovieList extends Component { static navigationOptions = { title: 'Movies', } //…

app/components/MovieList.js

<FlatList data={this.state.movies} ListHeaderComponent={<MainHeader/>} keyExtractor={item => item.name} renderItem={({item}) => <TouchableHighlight underlayColor={Colors.subtleAccent} activeOpacity={0.5} onPress={() => navigate('Movie', {name: item.name})} > <View> <ListItem name={item.name} image={item.image}/> </View> </TouchableHighlight> }/>

In render()

ExerciseCan you make a navigation transition from Movie to app/components/Actor ?

Steps: - Declare the screen in app/App.js - Use a TouchableHighlight to capture onPress in the actors list of <Movie/> - Provide an appropriate title in <Actor/> - Make the actor displayed based on props.navigation.state.params.name

Optional: have a look at https://reactnavigation.org/docs/navigators/stack#StackNavigatorConfigAnd tweak the navigation (Ideas: mode modal, add something to headerRight)

Networking

Networking

React-native uses the Fetch API

If you already know it, you are all set 🎉 

Let’s do it

componentDidMount() { return fetch(baseUrl + '/movies') .then((response) => response.json()) .then((responseJson) => { this.setState({ isLoading: false, movies: responseJson, }) }) .catch((error) => { console.error(error); })}

app/components/MovieList.js

Let’s do it

git checkout networking yarn start-server

(To discard your changes)

git reset HEAD --hard

Further reading

https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch

Docs:

https://github.com/mzabriskie/axios

Additional libraries:

Interactive

Our goal

Working with state

Not specific of React Native, we just use what we know of React 

Let’s do it

git checkout interactive

(To discard your changes)git reset HEAD --hard

Modify the native side

Cases where it is needed

Installing packages that have a native side Making our own changes or components Installing assets, such as fonts …

Simple case

Install this font

Let’s do it

"rnpm": { "assets": ["./assets/fonts/"] }

package.json

git checkout mod-native

(To discard your changes)

git reset HEAD --hard

Let’s do it

"rnpm": { "assets": ["./assets/fonts/"] }

package.json

react-native link

git checkout mod-native

(To discard your changes)

git reset HEAD --hard

Assets linked

Changes to be committed: (use "git reset HEAD <file>..." to unstage)

new file: android/app/src/main/assets/fonts/OleoScript-Bold.ttfnew file: android/app/src/main/assets/fonts/OleoScript-Regular.ttfmodified: ios/travolta.xcodeproj/project.pbxprojmodified: ios/travolta/Info.plistmodified: package.json

Summary:What is React Native Starting a project Working with Styles Layout Lists Navigation Networking Touching the native side

Thanks! @nacmartin [email protected]