WIFI
matrix-conference
portland
React Native Fundamentals
WELCOME FRIENDS!
Welcome
Connect
community.infinite.red
#CR-fundamentals-2019
Morning
8:00 - 8:30am
8:30 - 9:00am
9:00 - 9:30am
11:30 - 12:00pm
9:30 - 10:00am
Welcome
React Philosophy - ES6 - JSX
Hello World Basics - passing props
Play with components
10:00 - 10:30am
BREAK
Styles intro -💪Flex Yo Self exercises
10:30 - 11:00am
TextInput & managing local state
11:00 - 11:30am
More with state - buttons
🍽 LUNCH 🍽
12:00pm-1:00pm
Afternoon
1:00 - 1:15pm
1:45 - 2:00pm
Panel
2:30 - 3:00pm
Lists
2:00 - 2:30pm
State Management & useReducer
3:00 - 3:30pm
Componentize your life
3:00 -3:30pm
BREAK
3:30 - 4:00pm
Navigation
4:00 - 5:00pm
AsyncStorage - Wrap up - Questions - Misc
Context API & useContext hook
1:15 - 1:45pm
Let's talk about hooks, baby!
Let Us Meet You
-
What is your name?
-
Where are you from?
-
Programming background?
- Outside of programming, what do you enjoy?
Frank
von Hoven
-
Senior Software Engineer
-
React Native Newsletter
- Editor-in-Chief
Ryan Linton
-
Señor Software Engineer
-
Master of the Serious
React Native Philosophy
The React Story
What is React?
- UI as a function of state
- React virtual DOM, React Fiber
- By Facebook
What is React Native?
- Based on ReactJS
- Multiple Platforms
- "Learn once, write anywhere"
Software Goals
- Reusable
- Predictable
- Testable
- Scalable
Who Uses React Native?
Declarative
- Flexible
- Predictable
- Easier to debug
Declarative Code
- Imperative response:
- Go out of the north exit of the parking lot and take a left.
- Get on I-15 North until you get to the 12th street exit.
- Take a right off the exit like you’re going to Ikea.
- Go straight and take a right at the first light.
- Continue through the next light then take your next left.
- My house is #123.
“I’m right next to Wal-Mart. How do I get to your house from here?"
- Declarative response:
- My address is 123 Happy St., Portland, OR 97035
“I’m right next to Wal-Mart. How do I get to your house from here?"
Composability
- Small reusable components
- Combine components into "screens"
- Screens into App
ES6
ES6
New variable types
let
const
var
X
99% of the time
ES6
Arrow functions: ( ) => { }
ES5
ES6
binds function to
var sayName =
function() { }
const sayCity =
() => { }
() => { }
this
this.sayName.bind(this)
this.sayCity()
ES6
const getName = (name) => {
return name
}
Functions - explicit vs implicit
var getName = function(name) {
return name
}
ES5
ES6
const getName = (name) => name
EXPLICIT
IMPLICIT - only with
const getName = (name) =>
name
return
Implicit === No { } required
ES6
ES6
const sayName = (name) => console.log(name)
Arrow Function Parameters
const sayName = name => console.log(name)
const addTwo = (a, b) => console.log(a + b)
1 Parameter
( ) are optional
2+ Parameters
( ) are required
ES6
Object { destructuring }
const { name, hair } = person
const person = {
name: 'James',
hair: 'Brown',
height: "6'1"
}
// "James", "Brown"
console.log(name, hair)
ES6
Spread operator
console.log(person)
Spread an object
...
const employment = {
company: 'Infinite Red',
job: 'Software Engineer'
}
const basicInfo = {
firstName: 'Ryan',
lastName: 'Linton'
}
const person = {
...basicInfo,
...employment,
food: 'Tacos',
movies: 'Comedy'
}
console.log(person)
{
firstName: 'Ryan',
lastName: 'Linton',
company: 'Infinite Red',
job: 'Software Engineer',
food: 'Tacos',
movies: 'Comedy'
}
const person = {
...basicInfo,
...employment,
}
const person = {
...basicInfo,
}
const person = {
...basicInfo,
...employment,
food: 'Tacos',
}
JSX
JSX
- JSX adds XML-like syntax to JavaScript.
- Can use React Native without JSX.
JSX
This:
Compiles to:
<Text color="blue">
Click Me
</Text>
React.createElement(
Text,
{color: 'blue'},
'Click Me'
)
React.createElement(
type,
[props],
[...children]
)
createElement
Signature:
What makes this possible?
Babel is a JavaScript compiler
Babel
See it in action:
JSX vs. HTML
<View>
<Text>Hello World</Text>
</View>
<button onclick="myFunction()">
<div>
<p>Hello World</p>
</div>
<input type="text">
<TextInput />
<TouchableOpacity onPress={myFunction()} />
Embedding Expressions into JSX
const person = { name: ‘Chris’, age: 22 }
<View>
<Text>Hello { person.name }</Text>
</View>
// "Hello Chris"
JSX Children
<View>
<Text>Hello!</Text>
<Text>Good to see you here.</Text>
</View>
JSX
- HTML/XML-like structure in the same file as our JS code
- JSX is transformed into actual JS code (via Babel)
- tl;dr → JSX === Syntactic sugar
Candy Time!
JavaScript XML
- For a candy, JSX stands for...
Hello World 🌎
Hello World
- React Native Core Components
- Get app running
- Examine structure
- Create functional component
- Pass props
Preview
React Native Core Components
-
View
-
ScrollView
-
Text
-
TextInput
-
Image
-
TouchableOpacity
React Native vs HTML
HTML | React Native |
---|---|
<div> | <View> |
<p> | <Text> |
<input /> | <TextInput /> |
<button> | <TouchableOpacity /> |
$ git clone git@github.com:infinitered/PackingList2019.git
$ cd PackingList2019
---------------------------
$ yarn
or
$ npm i
---------------------------
$ react-native run-ios
or
$ react-native run-android
App.js
code
Hello World
- Get app running
- Examine structure
- Create functional component
- Pass props
Re-cap
Let's look at what comes with React Native...
Go crazy
- Take 15-20 min and explore components.
- Put your finished work in the workshop Slack channel.
- A winner will be chosen!
☕️ Break ☕️
Please return at 10:30am
Next Up:
Packing List App
Packing List App
Local State
Lesson 1
- Able to write to and read from local state
- Introduce TextInput component
Preview
- Introduce local state
Basic React Native Concepts
import React from 'react'
import { Text } from 'react-native'
class App extends React.Component {
constructor(props) {
super(props)
this.state = { name: 'Dog' }
}
render() {
return(
<Text>{this.state.name}</Text>
)
}
}
Creating state
Using constructor to declare state
Basic React Native Concepts
import React from 'react'
import { Text } from 'react-native'
class App extends React.Component {
state = { name: 'Dog' }
render() {
return(
<Text>{this.state.name}</Text>
)
}
}
Creating state
State using property initializers to declare state
Basic React Native Concepts - state
import React from 'react'
import { Text } from 'react-native'
class App extends React.Component {
state = { name: 'Dog' }
// lifecycle methods
render() {
return (
<Text>{this.state.name}</Text>
)
}
}
class
<App />
- Contains local state
- Lifecycle methods
- State is data
- Private
- Fully controlled by component
Basic React Native Concepts
import React from 'react'
import { Text } from 'react-native'
class App extends React.Component {
state = { name: 'Dog' }
render() {
return(
<Text onPress={() => this.setState({ name: 'Cat' })}>
{this.state.name}
</Text>
)
}
}
Updating state with
-
Component needs to be re-rendered with the updated state
setState
-
Enqueues changes to the component state
code
Button, Button, who's got the Button?
- Introduce "items" array into local state
Preview
- Able to add a new item into the items array
- Introduce TouchableOpacity
- Display items as a list
code
map()
- Creates a new array
- With the results of calling a provided function
- On every element in the calling array.
map()
[1, 2, 3]
Array →
[1, 2, 3].map()
.map() →
[1, 2, 3].map((item) => item)
.map() →
[1, 2, 3].map((item, index) => item + index)
.map() →
[1, 2, 3]
returns →
[1, 3, 5]
returns →
map()
{ items: ["Socks", "Shoes", "Jeans"] }
items.map((item) => ?)
Shoes
Socks
Jeans
items.map((item) => <Text>{item}</Text>)
Possible Options
- .concat()
- .push()
- ...
- .splice()
- .unshift()
- array[ i ]
Add a new item to the items array:
Buttons
Re-cap
- Introduce "items" array into local state
- Able to add a new item into the items array
- Introduce TouchableOpacity
- Display items as a list
Styling + Flexbox
Styling
Styles can be created one of three ways:
- inline
- object
- StyleSheet
Styling
inline styles
export App = () => {
return (
<View
style={{
width: 300,
hight: 150,
backgroundColor: "green"
}}
>
<Text style={{ color: "red" }}>Hello</Text>
</View>
)
}
Styling
style object
const styles = {
text: {
color: "red"
}
}
export App = () => {
return <Text style={styles.text}>Hello World</Text>
}
Styling
StyleSheet
// ... imports ⬆︎
export class App extends Component {
render () {
return (
<View style={styles.container}>
<Text style={styles.text}>Hello World</Text>
</View>
)
}
}
const styles = StyleSheet.create({
container: {
width: 300,
height: 130
},
text: {
color: "red"
}
})
Styling
Array of styles
// ... imports ⬆︎
export class App extends Component {
render () {
return <Text style={[ styles.text, styles.bigText ]}>Hello</Text>
}
}
const styles = StyleSheet.create({
text: {
color: 'red'
},
bigText: {
fontSize: 30
}
})
Styling
Combining inline styles with StyleSheet
// ... imports ⬆︎
export class App extends Component {
render () {
return (
<Text style={[ styles.text, styles.bigText ]}>
Hello
</Text>
)
}
}
const styles = StyleSheet.create({
text: {
color: 'red'
},
bigText: {
fontSize: 30
}
})
// ... imports ⬆︎
export class App extends Component {
render () {
return (
<Text style={[ styles.text, styles.bigText, {} ]}>
Hello
</Text>
)
}
}
const styles = StyleSheet.create({
text: {
color: 'red'
},
bigText: {
fontSize: 30
}
})
// ... imports ⬆︎
export class App extends Component {
render () {
return (
<Text style={[ styles.text, styles.bigText, { margin: 5 } ]}>
Hello
</Text>
)
}
}
const styles = StyleSheet.create({
text: {
color: 'red'
},
bigText: {
fontSize: 30
}
})
Styling
Dynamic styling
// ... imports ⬆︎
export class App extends Component {
state = { warning: true }
render() {
const fontColor = this.state.warning ? 'red' : 'black'
return (
<View>
<Text style={{ color: fontColor }}>Hello World</Text>
</View>
)
}
}
Styling
- Styles usually match CSS
- Prop names are camelCased
backgroundColor
background-color
vs
Styling
- width / height - number
- top / left / bottom / right - number
- padding - number
- margin - number
- borderWidth - number
- borderColor - color
Non-flex styles
<View/>
FlexBox
Flexbox is designed to provide a consistent layout on different screen sizes.
You will normally use a combination of:
-
flexDirection
-
alignItems
-
justifyContent
to achieve the right layout.
Who's used Flexbox
before today?
FlexBox
flex: number
container: {
flex: 1
},
box1: {
flex: 1,
backgroundColor: 'red'
},
<View style={styles.container}>
<View style={styles.box1} />
</View>
FlexBox
flex: number
container: {
flex: 1
},
box1: {
flex: 1,
backgroundColor: 'red'
},
box2: {
flex: 1,
backgroundColor: 'orange'
},
<View style={styles.container}>
<View style={styles.box1} />
<View style={styles.box2} />
</View>
FlexBox
flex: number
container: {
flex: 1
},
box1: {
flex: 1,
backgroundColor: 'red'
},
box2: {
flex: 2, // "Change flex from 1 to 2"
backgroundColor: 'orange'
},
<View style={styles.container}>
<View style={styles.box1} />
<View style={styles.box2} />
</View>
FlexBox
flexDirection
container: { flex: 1 }
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
- defines primary axis
- default is column
FlexBox
flexDirection
container: {
flex: 1,
flexDirection: 'row'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
- defines primary axis
- default is column
- row or column
FlexBox
justifyContent
Determines the distribution of children along the primary axis.
flexDirection: column
flexDirection: row
⬆
⬇
⬅ ➡
FlexBox
justifyContent:
- flex-start
- center
- flex-end
- space-around
- space-between
FlexBox
justifyContent:
container: {
flex: 1,
justifyContent: 'center'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'center'
FlexBox
justifyContent:
container: {
flex: 1,
justifyContent: 'flex-end'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'flex-end'
FlexBox
justifyContent:
container: {
flex: 1,
justifyContent: 'space-around'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'space-around'
FlexBox
justifyContent:
container: {
flex: 1,
justifyContent: 'space-between'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'space-between'
FlexBox
alignItems
Determines the distribution of children along the secondary axis.
flexDirection: column
⬆
⟷
⬇
⬅ ↕ ➡
alignItems: center
flexDirection: row
alignItems: center
Default - 'stretch'
FlexBox
alignItems:
container: {
flex: 1,
alignItems: 'center'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'center'
FlexBox
alignItems:
container: {
flex: 1,
alignItems: 'flex-end'
}
<View style={styles.container}>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
<Text>Hello World</Text>
</View>
'flex-end'
Flexbox Fun
Let's Play!
coding
exercise
Styling
- Inline
- Object
- StyleSheet
- FlexBox
Re-cap
🍽 Lunch 🍽
Please return at 1:00pm
Panel
Hooks!
Hooks!
- Lets you use state and other React features without writing a class
- Functions that let you “hook into” React state and lifecycle features from function components
- Don’t work inside classes — they let you use React without classes
useState()
- Allows you to use state in a functional component
- Can declare only one state variable at a time
- Able to set an initial state as an argument
- Use array destructuring to retrieve the value and setter
const [count, setCount] = useState(0)
How does it work???
Let's refactor our App component with useState
code
FlatList
FlatList
- Inherits from ScrollView
- Renders an array of items
FlatList
Requires 3 props:
-
data
-
renderItem
-
keyExtractor
code
able to check an item
coding
exercise
Hints
-
items: string[]
- ["Socks", "Shoes"]
-
items: object[]
- [{ name: "Socks, ?: ??? }, { name: "Shoes", ?: ??? }]
FlatList
- Intro to FlatList
- Add ability to "check" an item
Re-cap
Componentize
Componentize
Preview
- Create Button Functional Component
- Create ListInput Functional Component
- Clear items
- Use useState hook in ListInput Component
Current App
coding exercise
Re-cap
- Create Button Functional Component
- Create ListInput Functional Component
- Clear items
- Use useState hook in ListInput Component
Componentize
☕️ Break ☕️
Please return at 3:30pm
React State Museum
Reducer
Reducer
- The concept of a Reducer became popular with the rise of Redux as state management solution for React
- Basically reducers are there to manage state in an application
- A reducer is a function which takes two arguments:
- state
- action
Reducer
(state, action) => newState
const counterReducer = (state, action) => {
return state + 1
}
const counterReducer = (count, action) => {
return count + 1
}
state
Reducer
- Normally defined as an object { } with a `type` property
- Based on the `type` of the action, the reducer can perform conditional state transitions
action
Reducer
const counterReducer = (count, action) => {
if (action.type === 'INCREASE') {
return count + 1
}
if (action.type === 'DECREASE') {
return count - 1
}
return count
}
action
Reducer
const counterReducer = (count, action) => {
switch (action.type) {
case 'INCREASE':
return count + 1
case 'DECREASE':
return count - 1
default:
return count
}
}
action
Reducer
state as { }
-
The state processed by a reducer function is immutable
-
That means the incoming state is never directly changed; therefore, the reducer function always has to return a new state object
-
We can use the spread operator to create a new state object from the incoming state and the part we want to change
Reducer
const counterReducer = (state, action) => {
}
state as { }
const counterReducer = (state, action) => {
switch (action.type) {
case 'INCREASE':
return { ...state, count: state.count + 1 }
case 'DECREASE':
return { ...state, count: state.count - 1 }
default:
return state
}
}
useReducer Hook
const [state, dispatch] = useReducer(reducer, initialState)
dispatch
- Action object will tell how/what to update in the reducer state
- Pass the `action` object to the reducer function
dispatch({ type: "increment" })
const initialState = { count: 0 }
dispatch
reducer = (state, action) => {
switch (action.type) {
case 'increment':
return {count: state.count + 1}
case 'decrement':
return {count: state.count - 1}
default:
throw new Error('Nobody here by that name!')
}
}
const Counter = () => {
const [state, dispatch] = useReducer(reducer, initialState)
return (
<View>
<Text>Count: {state.count}</Text>
<Text onPress={() => dispatch({type: 'increment'})}>+</Text>
<Text onPress={() => dispatch({type: 'decrement'})}>-</Text>
</View>
)
}
Let's move our app's state into a reducer so that it can be shared between 2 components
code
Navigation
Preview
- React Navigation
- Stack Navigator
- Tabs Navigator
Navigation
react-navigation
The community solution to navigation is a standalone library that allows developers to set up the screens of an app with just a few lines of code
Navigation
Main types of navigators:
StackNavigator
SwitchNavigator
TabNavigator
DrawerNavigator
Navigation
npm i react-navigation --save
Step 1: Install react-navigation
yarn add react-navigation
or
Navigation
npm i react-native-gesture-handler --save
Step 1a: Install react-native-gesture-handler
yarn add react-native-gesture-handler
or
Navigation
Step 1b: Link react-native-gesture-handler
react-native link react-native-gesture-handler
under the hood
Navigation
createStackNavigator
Step 2: import createStackNavigator
import { createStackNavigator } from "react-navigation"
Step 3: import createAppContainer
import { createStackNavigator, createAppContainer } from "react-navigation"
Navigation
createAppContainer
- Containers are responsible for managing your app state and linking your top-level navigator to the app environment
- On Android, the app container uses the Linking API to handle the back button
- The container can also be configured to persist your navigation state
- Props of createAppContainer
Navigation
Step 4: Create navigation screens using components
const MyHomeRoutes = createStackNavigator({
Home: { screen: Home },
Info: { screen: Info }
},{
initialRouteName: "Home"
}
})
- StackNavigatorConfig: object
- RouteConfigs: object
createStackNavigator takes 2 arguments:
Navigation
StackNavigator - Basic Implementation
const Home = () => (
<Text>Hello from Home</Text>
)
const Info = () => (
<Text>Hello from Info</Text>
)
const AppStack = StackNavigator({
Home: { screen: Home },
Info: { screen: Info }
})
const App = createAppContainer(AppStack)
AppRegistry.registerComponent("App", () => App)
code
Tab Navigator
createBottomTabNavigator
import createBottomTabNavigator
import { createBottomTabNavigator } from "react-navigation"
Tab Navigator
Create Tab routes using screens or other navigators
const Tabs = createBottomTabNavigator(
{
Home: HomeScreen,
Input: InputScreen
},
{
initialRouteName: "Home",
tabBarOptions: {
activeBackgroundColor: "orange",
inactiveBackgroundColor: "yellow"
}
}
)
- BottomTabNavigatorConfig: object
- RouteConfigs: object
createBottomTabNavigator can also take 2 arguments:
Tab Navigator
code
Navigation
- React Navigation
Re-cap
- Stack Navigator
- Tabs Navigator
Context API
- Pass data deeply throughout your app without having to manually pass props down through multiple levels/components
Context API
const MyContext = React.createContext(defaultValue)
// Returns an object with 2 values:
// { Provider, Consumer }
// ...
const App = () => {
return (
<MyContext.Provider value={{ items: [] }}>
<ItemsScreen />
</MyContext.Provider>
)
}
AppRegistry.registerComponent(appName, () => App)
Context API
const ItemsScreen = () => {
// Notice this component isn't passed any props!
// Use the Consumer to grab the value from context
return (
<MyContext.Consumer>
{value =>
value.items.map(item => <Text>{item.name}</Text>)
}
</MyContext.Consumer>
)
}
Context API
-
We have to wrap our content in a MyContext.Consumer
-
Use the render props pattern – passing a function as a child – to retrieve the value and display it
-
Introduces some unnecessary nesting
-
useContext
useContext
const MyContext = React.createContext(defaultValue)
// Returns an object with 2 values:
// { Provider, Consumer }
// ...
const App = () => {
return (
<MyContext.Provider value={{ items: [] }}>
<ItemsScreen />
</MyContext.Provider>
)
}
AppRegistry.registerComponent(appName, () => App)
useContext
import React, { useContext } from 'react'
import { MyContext } from './somewhere'
// ...
const ItemsScreen = () => {
const value = useContext(MyContext)
}
import React, { useContext } from 'react'
import { MyContext } from './somewhere'
// ...
const ItemsScreen = () => {
const value = useContext(MyContext)
return value.items.map(item => <Text>{item.name}</Text>)
}
code
AsyncStorage
- Persist items in AsyncStorage
Preview
- Bring in AsyncStorage
- Lean Core
AsyncStorage
Main methods:
-
setItem
-
multiGet
-
getItem
-
multiSet
AsyncStorage
setItem(key: string, value: string, ?(error) => void)
setItem
getItem(key: string, ?(error, result: ?string) => void)
getItem
setItem("username", "FrankTheTank")
getItem("username", (error, result) => console.log(error, result))
AsyncStorage
multiSet(keyValuePairs: Array<Array<string>>, ?(errors) => void)
multiSet
multiSet(
[
["username", "FrankTheTank"],
["password", "password_snake_cased"]
],
(errors) => console.log(errors)
)
AsyncStorage
multiGet(keys: Array<string>, ?(errors, result: Array<Array<string>>) => void)
multiGet
multiGet(
["username", "password"],
(errors, result) => console.log(result)
)
// [
// ["username", "FrankTheTank"],
// ["password", "password_snake_cased"]
// ]
AsyncStorage
- Persist items in AsyncStorage
Re-cap
- Bring in AsyncStorage
- Lean Core
Playground
Packing List App
Questions?
Wrapping Up
Reactotron
Solidarity
Ignite
Fin
Thank you all!
CR: 2019 RN Fundamentals
By Infinite Red
CR: 2019 RN Fundamentals
React Native Fundamentals Workshop Chain React 2019
- 1,964