WIFI
matrix-conference
portland
WELCOME FRIENDS!
community.infinite.red
#CR-fundamentals-2019
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
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!
What is your name?
Where are you from?
Programming background?
“I’m right next to Wal-Mart. How do I get to your house from here?"
“I’m right next to Wal-Mart. How do I get to your house from here?"
New variable types
let
const
var
X
99% of the time
Arrow functions: ( ) => { }
ES5
ES6
binds function to
var sayName =
function() { }
const sayCity =
() => { }
() => { }
this
this.sayName.bind(this)
this.sayCity()
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
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
Object { destructuring }
const { name, hair } = person
const person = {
name: 'James',
hair: 'Brown',
height: "6'1"
}
// "James", "Brown"
console.log(name, hair)
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',
}
This:
Compiles to:
<Text color="blue">
Click Me
</Text>
React.createElement(
Text,
{color: 'blue'},
'Click Me'
)
React.createElement(
type,
[props],
[...children]
)
createElement
Signature:
Babel is a JavaScript compiler
See it in action:
<View>
<Text>Hello World</Text>
</View>
<button onclick="myFunction()">
<div>
<p>Hello World</p>
</div>
<input type="text">
<TextInput />
<TouchableOpacity onPress={myFunction()} />
const person = { name: ‘Chris’, age: 22 }
<View>
<Text>Hello { person.name }</Text>
</View>
// "Hello Chris"
<View>
<Text>Hello!</Text>
<Text>Good to see you here.</Text>
</View>
JavaScript XML
View
ScrollView
Text
TextInput
Image
TouchableOpacity
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
Let's look at what comes with React Native...
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
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
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 />
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
code
[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 →
{ items: ["Socks", "Shoes", "Jeans"] }
items.map((item) => ?)
Shoes
Socks
Jeans
items.map((item) => <Text>{item}</Text>)
Styles can be created one of three ways:
inline styles
export App = () => {
return (
<View
style={{
width: 300,
hight: 150,
backgroundColor: "green"
}}
>
<Text style={{ color: "red" }}>Hello</Text>
</View>
)
}
style object
const styles = {
text: {
color: "red"
}
}
export App = () => {
return <Text style={styles.text}>Hello World</Text>
}
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"
}
})
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
}
})
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
}
})
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>
)
}
}
backgroundColor
background-color
vs
Non-flex styles
<View/>
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.
flex: number
container: {
flex: 1
},
box1: {
flex: 1,
backgroundColor: 'red'
},
<View style={styles.container}>
<View style={styles.box1} />
</View>
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>
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>
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>
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>
- row or column
justifyContent
Determines the distribution of children along the primary axis.
flexDirection: column
flexDirection: row
⬆
⬇
⬅ ➡
justifyContent:
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'
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'
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'
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'
alignItems
Determines the distribution of children along the secondary axis.
flexDirection: column
⬆
⟷
⬇
⬅ ↕ ➡
alignItems: center
flexDirection: row
alignItems: center
Default - 'stretch'
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'
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'
coding
exercise
const [count, setCount] = useState(0)
code
Requires 3 props:
data
renderItem
keyExtractor
code
able to check an item
coding
exercise
items: string[]
items: object[]
coding exercise
(state, action) => newState
const counterReducer = (state, action) => {
return state + 1
}
const counterReducer = (count, action) => {
return count + 1
}
state
action
const counterReducer = (count, action) => {
if (action.type === 'INCREASE') {
return count + 1
}
if (action.type === 'DECREASE') {
return count - 1
}
return count
}
action
const counterReducer = (count, action) => {
switch (action.type) {
case 'INCREASE':
return count + 1
case 'DECREASE':
return count - 1
default:
return count
}
}
action
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
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
}
}
const [state, dispatch] = useReducer(reducer, initialState)
dispatch
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>
)
}
code
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
Main types of navigators:
StackNavigator
SwitchNavigator
TabNavigator
DrawerNavigator
npm i react-navigation --save
Step 1: Install react-navigation
yarn add react-navigation
or
npm i react-native-gesture-handler --save
Step 1a: Install react-native-gesture-handler
yarn add react-native-gesture-handler
or
Step 1b: Link react-native-gesture-handler
react-native link react-native-gesture-handler
under the hood
createStackNavigator
Step 2: import createStackNavigator
import { createStackNavigator } from "react-navigation"
Step 3: import createAppContainer
import { createStackNavigator, createAppContainer } from "react-navigation"
createAppContainer
Step 4: Create navigation screens using components
const MyHomeRoutes = createStackNavigator({
Home: { screen: Home },
Info: { screen: Info }
},{
initialRouteName: "Home"
}
})
createStackNavigator takes 2 arguments:
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
createBottomTabNavigator
import createBottomTabNavigator
import { createBottomTabNavigator } from "react-navigation"
Create Tab routes using screens or other navigators
const Tabs = createBottomTabNavigator(
{
Home: HomeScreen,
Input: InputScreen
},
{
initialRouteName: "Home",
tabBarOptions: {
activeBackgroundColor: "orange",
inactiveBackgroundColor: "yellow"
}
}
)
createBottomTabNavigator can also take 2 arguments:
code
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)
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>
)
}
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
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)
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
Main methods:
setItem
multiGet
getItem
multiSet
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))
multiSet(keyValuePairs: Array<Array<string>>, ?(errors) => void)
multiSet
multiSet(
[
["username", "FrankTheTank"],
["password", "password_snake_cased"]
],
(errors) => console.log(errors)
)
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"]
// ]