Getting Started with React Native

Based on the complete React Native and Redux class on Udemy

Step1: install all dependencies

  1. Install Xcode
  2. Install Homebew -> brew.sh
$ /usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
$ brew update

3. Install Node/NPM

$ brew install node
$ node -v

4. Install watchman : to watch our hard drive for file changes

$ brew install watchman

5. Install RN CLI

$ npm install -g react-native-cli

Step2: create your first react-native project

$ react-native init albums
$ cd albums
$ react-native run-ios

Step3: make changes to your project

  1. Download WebStorm or Atom IDE 
    https://www.jetbrains.com/webstorm/download/
    https://atom.io

2. Install and configure ‘ESLint’ for IDE (for code quality check)
2.1 WebStorm, then add Flow to Language & Framework
https://blog.jetbrains.com/webstorm/2016/11/using-flow-in-webstorm/
2.2 Atom

go inside your project, then in terminal type the following to install npm module

albums$ npm install --save-dev eslint-config-rallycoding

then create a ‘.eslintrc’ file under ‘root’ folder

3. Open project folder, you will see the following screen

!!! side note : differences between ‘React’ and ‘React Native’

!!! side note: components in ‘React’ and ‘React-Native’ is generated using JSX

!!! side note: use ‘Babel’ a tool to turn ‘JSX’ into normal ‘javascript’

4. inside ‘index.ios.js’ let’s get started

//index.ios.js place code in here for iOS
//Import a library to help create a Component
import React from ‘react’;
import { Text, AppRegistry } from ‘react-native’;
//create a Component
const App = () => (
<Text>Something</Text>
);
//Render it to the device
AppRegistry.registerComponent(‘test2’, () => App);

Step4: Application Outline

Step5: Create Header Component

  1. create file header.js inside albums/src/components
//path> src/components/header.js
//Import a library to help create a Component
import React from 'react';
import { Text } from 'react-native';
//create a Component
const Header = () => {
return <Text>Albums!</Text>
};
//Make the component available to other parts of the app
export default Header;

2. make change to ‘index.ios.js’

//index.ios.js place code in here for iOS
//Import a library to help create a Component
import React from ‘react’;
import { AppRegistry } from ‘react-native’;
import Header from ‘./src/components/header’;
//create a Component
const App = () => (
<Header />
);
//Render it to the device
AppRegistry.registerComponent(‘albums’, () => App);

3. Then refresh ios simulator

4. style the header

//path> src/components/header.js
//Import a library to help create a Component
import React from ‘react’;
import { Text } from ‘react-native’;
//create a Component
const Header = () => {
const { textStyle } = styles;
return <Text style = {textStyle}>Albums!</Text>
};
const styles = {
textStyle: {
fontSize: 20
}
};
//Make the component available to other parts of the app
export default Header;

5. style more components

//path> src/components/header.js
//Import a library to help create a Component
import React from ‘react’;
import { Text, View } from ‘react-native’;
//create a Component
const Header = () => {
const { textStyle, viewStyle } = styles;
return (
<View style = {viewStyle}>
<Text style = {textStyle}>Albums!</Text>
</View>
);
};
const styles = {
viewStyle: {
backgroundColor: ‘#CECBCB’
},
textStyle: {
fontSize: 20
}
};
//Make the component available to other parts of the app
export default Header;

6. introduction to ‘flexbox’ 
- justifyItems (vertical): ‘flex-start’, ‘center’, ‘flex-end’
- alignItems (horizontal): ‘flex-start’, ‘center’, ‘flex-end’

//path> src/components/header.js
//Import a library to help create a Component
import React from ‘react’;
import { Text, View } from ‘react-native’;
//create a Component
const Header = () => {
const { textStyle, viewStyle } = styles;
return (
<View style = {viewStyle}>
<Text style = {textStyle}>Albums!</Text>
</View>
);
};
const styles = {
viewStyle: {
backgroundColor: ‘#F8F8F8s’,
justifyContent: ‘center’,
alignItems: ‘center’,
height: 60,
paddingTop: 15,
shadowColor: ‘#000’,
shadowOffset: {width:0, height: 2},
shadowOpacity: 0.2
},
textStyle: {
fontSize: 20
}
};
//Make the component available to other parts of the app
export default Header;

7. making header reusable

Step6: HTTP Requests with React Native

  1. Idea

2. add ‘AlbumList.js’ under ‘src/components/’

3. create class based components (becase we want to handle data from http request)

in this step we refactor ‘AlbumList.js’ from functional component to class-based component

4. Life-cycle method : how to debug the application

  • edit ‘AlbumList.js’ to include code to for debugging log
  • click ‘command + D’ you will see below screen and the debugger screen pops up, then click ‘debug JS remotely’

when we put ‘debugger;’ in the code it will run app in debug mode and stop where you put the code

5. Network Requests : make a http ‘get’ request to the backend to get data objects

here’s what has happened

the big issue is sometimes the page gets rendered faster than when the data came, we need to fix this issue by using ‘Component State Level’ to wait for returned data that will get rendered again

6. Component Level State

we sees that the page gets rendered 2 time with the 1st ‘albums’ array with 0 objects but 2nd ‘albums’ array with 5 objects

here is the api to get sample ‘json’ data > http://rallycoding.herokuapp.com/api/music_albums

7. more on ‘Component Level State’

8. Rendering a list of components

9. Displaying individual albums: refactor code so that we can separate AlbumDetail

//path> src/components/AlbumList.js
import React, { Component } from ‘react’;
import { Text, View } from ‘react-native’;
import axios from ‘axios’;
import AlbumDetail from ‘./AlbumDetail’;
//create class here
class AlbumList extends Component {
state = {albums: []}; //set empty state
componentWillMount(){
console.log(‘componentWillMount in AlbumList’);
axios.get(‘https://rallycoding.herokuapp.com/api/music_albums')
.then(response => this.setState({albums: response.data}));
//setState will help rerendering this page when new data come
}
renderAlbums() {
return this.state.albums.map(album => <AlbumDetail key={album.title} album={album} />);
}
render() {
console.log(this.state);
return(
<View>
{this.renderAlbums()}
</View>
);
}
}
export default AlbumList;

10. fantastic reusable components — the card

  • here is our game plan
withour reusable components
alternative layout with reusable components
//path> src/components/Card.js
import React, { Component } from ‘react’;
import { Text, View } from ‘react-native’;
//create functional component here
const Card = () => {
return(
<View>
</View>
);
};
export default Card;

11. styling card

//path> src/components/Card.js
import React, { Component } from ‘react’;
import { Text, View } from ‘react-native’;
//create functional component here
const Card = () => {
return(
<View style={styles.ContainerStyle}>
</View>
);
};
const styles = {
ContainerStyle: {
borderWidth: 1,
borderRadius: 2,
borderColor: ‘#ddd’,
borderBottomWidth: 0,
shadowColor: ‘#000’,
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop: 10
},
}

12. passing components as props

//path> src/components/Card.js
import React, { Component } from ‘react’;
import { Text, View } from ‘react-native’;
//create functional component here
const Card = (props) => {
return(
<View style={styles.ContainerStyle}>
{props.children}
</View>
);
};
const styles = {
ContainerStyle: {
borderWidth: 1,
borderRadius: 2,
borderColor: ‘#ddd’,
borderBottomWidth: 0,
shadowColor: ‘#000’,
shadowOffset: {width: 0, height: 2},
shadowOpacity: 0.1,
shadowRadius: 2,
elevation: 1,
marginLeft: 5,
marginRight: 5,
marginTop: 10
},
}
export default Card;

then make change to AlbumDetail.js

//path> src/components/AlbumDetail.js
import React, { Component } from ‘react’;
import { Text, View } from ‘react-native’;
import Card from ‘./Card’
//create functional component here
const AlbumDetail = (props) => {
return(
<Card>
<Text>{props.album.title}</Text>
</Card>
);
};
export default AlbumDetail;

13. dividing cards into sections

Step7: Handling Component Layout

7.1 mastering layout with flexbox

Here is what we want
if we ignore styling
we can do justifyContent

7.2 positioning of elements on mobile

here’s what we have to do

7.3 more on styling

7.4 images with React Native

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image } = album;
const { thumbnailStyle, headerContentStyle } = styles;
return (
<Card>
<CardSection>
<View>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
thumbnailStyle: {
height: 50,
width: 50
}
};
export default AlbumDetail;

7.5 displaying album artwork

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image, image } = album;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle,
headerTextStyle, imageStyle } = styles;
return (
<Card>
// header section
<CardSection>
<View style={thumbnailContainerStyle}>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
// image section
<CardSection>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
imageStyle: {
height: 300,
flex: 1,
width: null
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
}
};
export default AlbumDetail;

7.6 making content scrollable

step1: select which part of the app is scrollable

select return View inside AlbumList.js

step2: import react-native library -> use scroll view

//path> src/components/AlbumList.js
import React, { Component } from 'react';
import { Text, ScrollView } from 'react-native';
import axios from 'axios';
import AlbumDetail from './AlbumDetail';
//create class here
class AlbumList extends Component {
state = { albums: [] }; //set empty state
componentWillMount() {
console.log('componentWillMount in AlbumList');
axios.get('https://rallycoding.herokuapp.com/api/music_albums')
.then(response => this.setState({ albums: response.data }));
//setState will help rerendering this page when new data come
}
renderAlbums() {
return this.state.albums.map(album => <AlbumDetail key={album.title} album={album} />);
}
renderAlbums2() {
console.log('calling renderAlbums2');
return this.state.albums.map(album => <AlbumDetail key={album.title} album={album} />);
}
render() {
console.log(this.state);
return (
<ScrollView>
{ this.renderAlbums() }
</ScrollView>
);
}
}
export default AlbumList;

now we get the scrollable app

7.7 handling user input with buttons

add button in cardsection of ‘AlbumList.js’

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
import Button from './Button'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image, image } = album;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle,
headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</CardSection>

<CardSection>
<Button />
</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
imageStyle: {
height: 300,
flex: 1,
width: null
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
}
};
export default AlbumDetail;

add button.js under ‘src/components’

//src/components/button.js
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = () => {
return (
<TouchableOpacity>
<Text>Click me!!!</Text>
</TouchableOpacity>
);
};
export default Button;

7.8 styling of buttons with UX considerations

//src/components/button.js
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = () => {
const { buttonStyle, texStyle } = styles;
return (
<TouchableOpacity style={buttonStyle}>
<Text style={texStyle}>
Click me!!!
</Text>
</TouchableOpacity>
);
};
const styles = {
texStyle: {
alignSelf: 'center',
color: '#007aff',
fontSize: 16,
fontWeight: '600',
paddingTop: 10,
paddingBottom: 10
},
buttonStyle: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#fff',
borderRadius: 5,
borderWidth: 1,
borderColor: '#007aff',
marginLeft: 5,
marginRight: 5
}
}
export default Button;

7.9 responding to user input

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
import Button from './Button'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image, image } = album;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle,
headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</CardSection>
<CardSection>
<Button onPress={() => console.log({ title })} />
</CardSection>

</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
imageStyle: {
height: 300,
flex: 1,
width: null
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
}
};
export default AlbumDetail;

make change to Button.js

//path> src/components/Button.js
import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ onPress }) => {
const { buttonStyle, texStyle } = styles;
return (
<TouchableOpacity
onPress={onPress}
style={buttonStyle}
>
<Text style={texStyle}>
Click me!!!
</Text>
</TouchableOpacity>
);

};
const styles = {
texStyle: {
alignSelf: 'center',
color: '#007aff',
fontSize: 16,
fontWeight: '600',
paddingTop: 10,
paddingBottom: 10
},
buttonStyle: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#fff',
borderRadius: 5,
borderWidth: 1,
borderColor: '#007aff',
marginLeft: 5,
marginRight: 5
}
}
export default Button;

whenever you click a button, there will be a log in the console

7.10 linking between mobile apps : we want to take user to open mobile browser when they click the link

edit ‘AlbumDetail.js’ and add Linking button to open url

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image, Linking } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
import Button from './Button'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image, image, url } = album;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle,
headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</CardSection>
<CardSection>
<Button onPress={() => Linking.openURL(url)} />
</CardSection>

</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
imageStyle: {
height: 300,
flex: 1,
width: null
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
}
};
export default AlbumDetail;

7.11 setting button text by props

//path> src/components/AlbumDetail.js
import React from 'react';
import { Text, View, Image, Linking } from 'react-native';
import Card from './Card'
import CardSection from './CardSection'
import Button from './Button'
//create functional component here
const AlbumDetail = ({ album }) => {
const { title, artist, thumbnail_image, image, url } = album;
const { thumbnailStyle, headerContentStyle, thumbnailContainerStyle,
headerTextStyle, imageStyle } = styles;
return (
<Card>
<CardSection>
<View style={thumbnailContainerStyle}>
<Image
style={thumbnailStyle}
source={{ uri: thumbnail_image }}
/>
</View>
<View style={headerContentStyle}>
<Text style={headerTextStyle}>{title}</Text>
<Text>{artist}</Text>
</View>
</CardSection>
<CardSection>
<Image
style={imageStyle}
source={{ uri: image }}
/>
</CardSection>
<CardSection>
<Button onPress={() => Linking.openURL(url)}>
Buy Now
</Button>

</CardSection>
</Card>
);
};
const styles = {
headerContentStyle: {
flexDirection: 'column',
justifyContent: 'space-around'
},
headerTextStyle: {
fontSize: 18
},
thumbnailStyle: {
height: 50,
width: 50
},
imageStyle: {
height: 300,
flex: 1,
width: null
},
thumbnailContainerStyle: {
justifyContent: 'center',
alignItems: 'center',
marginLeft: 10,
marginRight: 10
}
};
export default AlbumDetail;

then you need to make ‘Button.js’ truely reuseable

import React from 'react';
import { Text, TouchableOpacity } from 'react-native';
const Button = ({ onPress, children }) => {
const { buttonStyle, texStyle } = styles;
return (
<TouchableOpacity
onPress={onPress}
style={buttonStyle}
>
<Text style={texStyle}>
{children}
</Text>
</TouchableOpacity>
);
};
const styles = {
texStyle: {
alignSelf: 'center',
color: '#007aff',
fontSize: 16,
fontWeight: '600',
paddingTop: 10,
paddingBottom: 10
},
buttonStyle: {
flex: 1,
alignSelf: 'stretch',
backgroundColor: '#fff',
borderRadius: 5,
borderWidth: 1,
borderColor: '#007aff',
marginLeft: 5,
marginRight: 5
}
}
export default Button;

7.12 app wrap-up

  • state only available to Class object, only update it with ‘this.setState()’

Step8: Authentication with Firebase

8.1 start new application by typing

$ react-native init auth
$ react-native run-ios

8.2 then open Atom to edit code (we want ios and Android to use the same code) -> create ‘app.js’ inside ‘src’ folder

8.3 copying reusable components -> components from previous section put inside ‘src/components/common’

8.4 what is Firebase?

then you will see the following screen

choose ‘email/password’ for the signup method

8.5 Firebase client setup

  • install Firebase through npm
$ npm install --save firebase

on Firebase click ‘web setup’

edit ‘app.js’ to add life-cycle method to get user logged in before proceeding to the rendered page

//app.js
import React, { Component } from 'react';
import { View, Text } from 'react-native';
import firebase from 'firebase';
import { Header } from './components/common';
class App extends Component {
// need to add Life-Cycle method
componentWillMount() {
firebase.initializeApp({
apiKey: "AIzaSyB7elfzAI02vPbasgtvN3BdQ4jAjBL3IQw",
authDomain: "auth-1f3eb.firebaseapp.com",
databaseURL: "
https://auth-1f3eb.firebaseio.com",
projectId: "auth-1f3eb",
storageBucket: "auth-1f3eb.appspot.com",
messagingSenderId: "201442596072"
});
}
render() {
return (
<View>
<Header headerText="Authentication" />
<Text>An App!</Text>
</View>
);
}
}
export default App;

8.6 Login form scaffolding

create a login form

//LoginForm.js
import React, { Component } from 'react';
import { View } from 'react-native';
import { Button, Card, CardSection } from './common';
class LoginForm extends Component {
render() {
return (
<Card>
<CardSection />
<CardSection />
        <CardSection>
<Button>
Login
</Button>
</CardSection>
</Card>
);
}
}
export default LoginForm;

in ‘app.js’ add LoginForm

//app.js
import React, { Component } from 'react';
import { View } from 'react-native';
import firebase from 'firebase';
import { Header } from './components/common';
import LoginForm from './components/LoginForm'
class App extends Component {
// need to add Life-Cycle method
componentWillMount() {
firebase.initializeApp({
apiKey: 'AIzaSyB7elfzAI02vPbasgtvN3BdQ4jAjBL3IQw',
authDomain: 'auth-1f3eb.firebaseapp.com',
databaseURL: 'https://auth-1f3eb.firebaseio.com',
projectId: 'auth-1f3eb',
storageBucket: 'auth-1f3eb.appspot.com',
messagingSenderId: '201442596072'
});
}
render() {
return (
<View>
<Header headerText="Authentication" />
<LoginForm />
</View>
);
}
}
export default App;

after successfully run, you will see

8.7 Handling user inputs

edit ‘LoginForm.js’

import React, { Component } from 'react';
import { View, TextInput } from 'react-native';
import { Button, Card, CardSection } from './common';
class LoginForm extends Component {
render() {
return (
<Card>
<CardSection>
<TextInput style={{ height: 20, width: 20 }} />
</CardSection>

<CardSection />
<CardSection>
<Button>
Login
</Button>
</CardSection>
</Card>
);
}
}
export default LoginForm;

you will see

8.8 More on handling user inputs

8.9 How to create controlled components

8.10 Making text inputs from scratch

8.11 A focus on passing props

8.12 Making the input pretty

8.13 Wrapping up the inputs

8.14 Password inputs

Step9: Processing Authentication Credentials

Step10: Digging Deeper with Redux

Step11: Back to React

Step12: Rendering List the Right Way

Step13: Not Done Yet

Step14: Handling Data in React vs Redux

Step15: Don’t Mutate that State

Step16: Navigating Users Around

Step17: Firebase as a Data Store

Step18: Code Reuse — Edit vs Create

Step19: Bonus

TROUBLESHOOT

  1. After installing ESLint if you get ‘run-ios’ not recognized, you need to reinstall react-native and update to the latest version
step1: remove 'node_modules' folder from the project
step2:
$ npm install
$ npm install --save react-native@latest
$ npm install --save react@latest

2. Interesting development tools

1. JS playground with real-time feedbacks
https://stephengrider.github.io/JSPlaygrounds/

3. Interesting framework
3.1 Meteor and React for Realtime Apps

Show your support

Clapping shows how much you appreciated Dr. Warodom Khamphanchai’s story.