CS50 React Native
Table of Contents
Lecture 0: JS
- Introduction
- Course information
- Syntax
- Types
- dynamic types
- primitive types (no methods, immutable)
- undefined
- null
- boolean
- number
- string
- (symbol)
- objects
- Typecasting
- explicit
- implicit
- == (coerces the types) vs === (requires equivalent types)
- falsy
- undefined
- null
- false
- +0, -0, NaN
- ""
- truthy
- {}
- []
- everything else
- Type demo
- typeof operator
- Objects
const o = new Object()
ORconst o2 = {}; o2.firstName = ''
ORconst o3 = { firstName: '', greet(): function() {} }
- Arrays, Functions - are objects
- Everything but primitive datatypes are objects
- Anything between [] when indexing will be coerced to a string. E.g.
o[1]
->o["1"]
- Primitives are immutable, objects are mutable and stored by reference
Object.assign({}, o)
-> shallowCopy. Merges 1st arg into o.
- Objects demo
- Object mutation
Object.assign(target, source)
: The Object.assign() method copies all enumerable own properties from one or more source objects to a target object. It returns the modified target object.
- Prototypical inheritance
- Each objects stores a reference to its prototype (
obj.__proto__
) - Properties/methods defined most tightly have most priority
- JS will automatically “box” (wrap) primitive values so you have access to methods.
42 instanceof Number
->false
- Prototypical inheritance (reference to prototype instead of copies) is used to save resources.
- Danger: if you change the prototype it changes for every single value of that type. DON’T change prototype.
- Each objects stores a reference to its prototype (
- Scope
- variable lifetime
- lexical scoping (var): declared until their function ends
- block scoping (const, let)
- variables without cost/let are global
- hoisting - take the definition and hoist it to the top of the file
- var declaration
- function definition
- variable lifetime
- JS engine
- read entire file, throw a syntax error if found and store functions will be saved in memory
- execute it
- Global Object
- all variables and functions are parameters and methods on the global object:
- Browser:
window
- Node.js:
global
- Browser:
- all variables and functions are parameters and methods on the global object:
- Closures
- functions that refer to variables declared by parent function
- possible because of scoping
Lecture 1: JS, ES6
- IIFE: immediately invoked function expression
- creates closure
- doesn’t add or modify to global object
- first-class functions: functions are treated the same way as any other value
- can be used as value, passed as arguments, returned
- allows for creation of higher-order functions
- either takes one or more functions as arguments or returns a function
- map(), filter(), reduce()
- JS is synchronous and single-threaded
- a function that takes a long time to load will make the page unresponsive
- Asynchronous JS:
- Execution Stack
- Functions invoked by other functions get added to the stack
- When functions complete, they are removed from the stack and frame below continues executing
- Browser APIs: get run by the browser, not necessarily by JS itself
- setTimeout()
- fetch(), XMLHttpRequest(), JQuery.ajax()
- DB Calls
- Function Queue: is for API functions. As soon as the stack is empty whatever is in the queue will be put on the stack.
- Event Loop:
- check if there is anything on the stack
- check if there is anything in the function queue ready to go onto the stack
- Execution Stack
- Callback:
- Control the flow of async calls
- Execute a function once async call returns a value
- Promises:
fetch().then().then(),catch()
- Async/Await
- Introduced in ES2017
- Allows to write async code as if it were synchronous
- this:
- refers to an object that’s set at the creation of a new execution context
- in the global exec context, refers to global context
- if function is a method of an objects, it is the object the method is bound to
- Set
this
manually:- bind() - binds a object to an method explicitly and returns the fct
- call() - calls a function with given
this
and arguments - apply() - calls a function with given
this
and arguments provided as array - =>
this
doesn’t have an own binding
Lecture 2: React, Props, State
- Classes
- introduced in ES6
new, constructor, extends, super
get size()
-> getter for size property
- React:
- allows to write declarative views that “react” to changes in data
- allows to abstract complex problems into smaller components
- Imperative vs. Declarative: How vs What
- React:
- Declarative
- Performant:
- reconciliation - sync changes in the app state to the DOM for better performance:
- reconstructs virtual DOM
- diffs the virtual DOM against the DOM
- reconciliation - sync changes in the app state to the DOM for better performance:
- Writing React:
- JSX:
- XML like syntax that transpiles to JS
- lowercase tags are treated as HTML/SVG tags, uppercase as custom components
- Components are just functions:
- Returns a node (something React can render, e.g. a )
- receives an object of properties that are passed to the element
- Props:
- changes to the props will cause a recomputation of the returned node (render)
- Unlike HTML, props can be any JS value
- JSX:
- arrow notation has an implicit return if you don’t have braces
- State:
- internally-managed configuration for any component
this.state
,this.setState()
:- calls are batched and run asynchronously
- pass a function (
(prevState) => {count: prevState.count + 1}
) for an immediate update
- changes in state also cause re-renders
class App extends React.Component {
constructor(props) {
super(props)
this.state = {
count: 0,
}
}
increaseCount() {
this.setState({count: this.state.count + 1})
}
render() {
<div>
<button onClick={() => this.increaseCount()}>Increase</button>
<h2>{this.props.count}</h2>
</div>
}
}
...arr
pulls out all the values out ofarr
- React paradigm: if you ever change something, replace it with something new.
Lecture 3: React Native
- React Native:
- JS is bundled:
- transpiled and minified
- separate threads for UI, layout and JS
- communicate asynchronously through a “bridge”:
- JS thread will request UI elements to be shown
- JS thread can be blocked and UI will still work
- JS is bundled:
- Differences between RN and React
- Base components
- Style
- No browser APIs
- CSS animations, Canvas, SVG
- Some have been polyfied
- Navigation
- React Native Components:
- div -> View
- span -> Text: all text must be wrapped by this
- button -> Button
- ScrollView
- Style
- CSS like properties,
style
prop - Flexbox layout with vertical placement default
- Length are unitless umbers
StyleSheet.create()
- CSS like properties,
- Event Handling
- Unlike web, not every component has every interaction
- Only a few “touchable” components
- Button
- Touchable*
- Components
- Return a node (something that can be rendered)
- Represent a discrete piece of the UI
- “All React components must act like pure functions with respect to their props.”
- Two types
- Stateless Functional Components (SFC) a.k.a. Pure Function Component.
- Pure because it should not have any side effects like setting values or updating arrays…
- Any change in props will cause the function to be re-invoked
- subclass of React.Component
- Can maintain their own state
- Have lifecycle methods
- Rendering now becomes a function of props and class properties
- Stateless Functional Components (SFC) a.k.a. Pure Function Component.
- Component Lifecycle
- Mount -> Update Cycle -> Unmount
- Mount
constructor(props)
render()
- return a node
componentDidMount()
- Do anything that isn’t needed for UI (async actions, timers, …)
- Setting state here will cause a re-render before updating the UI
- Update:
componentWillReceiveProps(nextProps)
- update any state fields that rely on props
shouldComponentUpdate(nextProps, nextState)
- compare changed values, return true if the component should render
- If returned false, the update cycle terminates
- Almost always a premature optimization
- compare changed values, return true if the component should render
render()
componentDidUpdate(prevProps, prevState)
- Do anything that isn’t needed for UI (network requests, etc.)
- Unmount
componentWillUnmount()
- clean up
- remove event listeners
- invalidate network requests
- clear timeouts/intervals
- clean up
- Expo
- Snack - run React Native in the browser
- XDE - a GUI to serve, share, publish Expo projects
- CLI for the same
- Client - run on the phone while developing
- SDK
- Import/Export
- Split components into their own files:
- Helps organize projects
- Export and the import the component
- Split components into their own files:
// Count.js
export const num = 50
export const Count
const Other
export default Other
// App.js
import {Count, num} from './Count.js'
import Other from './Count.js' // import whatever the default is in Count.js and call it 'Other'
// or import Other, {num} from './Count.js'
export default class App extens React.Component
- PropTypes
- React can validate the types of component props at runtime
- Helps document your components’ APIs
- Only runs in development mode
import PropTypes form 'prop-types'
Count.propTypes = {
count: PropTypes.number.isRequired
}
// OR preferred:
class Count extends React.Component {
static propTypes = {
count: PropTypes.number.isRequired,
}
}
Lecture 4: Lists, User Input
- In mobile, as opposed to web, you have to take care of scrolling manually
- ScrollView:
- Will render all of its children before appearing
- To render an array of data, use
.map()
- FlatList:
- virtualized: only renders what’s needed at a time
- Only the visible rows are rendered in the first cycle.
- props:
data
,renderItem
(obj => {...}
) - only updates if props are changed
- immutability is important
- virtualized: only renders what’s needed at a time
- SectionList
- Like
FlatList
with additional support for sections - Instead of
data
prop, definesections
- Each section has it’s own data array
- Each
section
can override therenderItem
function
- Pass a
renderSectionHeader
function for section headers
- Like
- User Input
- Controlled (by react) vs. uncontrolled component (by the DOM)
- Where is the source of the truth for the value of an input
- React recommends always using controlled components
<TextInput />
: Passvalue
andonChangeText
props
- Controlled (by react) vs. uncontrolled component (by the DOM)
Lecture 5: User Input, Debugging
- test for a number:
+variable
-> number / 0 / NaN - Validating Input
- Conditionally set state based on input value
- Validate form after changing single input value
this.setState()
can take a callback as a second argument (validator), that gets executed after setting the state.- or
componentDidUpdate()
which is called aftersetState()
- KeyboardAvoidingView
// generic update function - less efficient than genering all handlers as class properties!
getHandler = key => val => {
this.setState({[key]: val})
}
<TextInput onChangeText={this.getHandler('name')} />
- KeyboardAvoidingView
- Debugging
- aggressive:
console.error()
orthrow new Error('...')
- warnings:
console.warn()
are yellow banners and do not appear in production mode console.log()
might have problems with circular JSON, unless run in remote debugger
- aggressive:
- Chrome Devtools:
- Hardware / Shake my device / remote debugger
- React Native Inspector
- Hardware / Shake my device / toggle native inspector
- react-devtools
npm install -g react-devtools
- run:
react-devtools
- External libs: since RN is native JS, you can add any JS library
Lecture 6: Navigation
yarn add react-navigation
SwitchNavigator
: function that returns a component to be returned inApp.render()
- convention is to have
./screens/${NAME}Screen.js
<AppNavigator screenProps={} />
-screenProps
are made available to every screen component in the navigator- good for prototyping
- bad performance for big applications, better use Redux
StackNavigator
. Supports history.static navigationOptions = { ... }
for a component
- You can pass parameters in Navigators between routes:
this.props.navigation.navigate('RouteName', {
paramName: 'value-of-param'
});
this.props.navigation.setParams({
paramName: 'new-value-of-param',
});
this.props.navigation.getParam('paramName', 'default-value');
TouchableOpacity
to make a view pressable.StackNavigator.push()
instead ofnavigate()
for pushing a new screen even if on the same screen.- Composing navigators
- Never render a navigator inside a screen. Only in the top level component.
- You can
navigate()
to a navigator as well (goes toinitialRouteName
)
- TabNavigator
- The state of inactive screens is maintained
- Use icons instead of only labels:
yarn add react-native-vector-icons
Lecture 7: Data
- https://randomuser.me
fetch(url, config)
is polyfiled.config
is optional. Returns a promise.- Promises
- .
then()/.catch()
- .
- Async/Await: within an
async
function we canawait
the value of another async function or Promise.- Use
try/catch
to handle errors.
- Use
- componentDidMount()
fetch(url)
- extract json
this.setState()
- Transforming data
- doing it early gives us an abstraction layer and is more efficient, therefore:
- api.js
- fetchUsers()
- processContanct() // transformation
- Authentication
- to prove ID
- HTTP status codes
- check for
response.ok
- check for
- JSX: TAG
parameter={true}
shortcut isparameter
Lecture 8: Expo Components
- Maps and Location
- <Expo.MapView />
let {status} = Expo.Permissions.askAsync(Expo.Permissions.LOCATION)
- Expo.Location.getCurrentPositionAsync({})
- Expo.Location.geocodeAsync(
{address}
) - Expo.Location.reverseGeocodeAsync(location.coords)
- <Expo.MapView.Marker />
- Compass
<Image source={require("./img.png")} />
<ImageBackground source={require("./bg.png")} />
- Expo.Magnetometer.addListener((v) => { … })
- 60FPS refresh rate on phone means 1000/60 = 16ms for updating interval
- Expo.Magnetometer.removeAllListeners()
- Audio / Video
<Expo.Video source={require(fn)}
resizeMode="cover"
shouldPlay={true}
ref={...}
onPlaybackStatusUpdate={status => {
if (status.didJustFinish) {
}
} />
await Expo.Audio.setAudioModeAsync({})
_setupAsync() = async() => {
await Promise.all([
this._loadAssetsAsync(), // require all the videos
this._setAudioModeAsync(),
this._loadFontsAsync()
]);
this.setState({ isReady: true })
}
return (<Expo.AppLoading />); // splashscreen
- A photo editor
- await Expo.Permissions.askAsync(Expo.Permissions.CAMERA_ROLL)
- check for
status === 'granted'
- check for
- Expo.ImagePicker.launchImageLibraryAsync()
- Expo.ImagePicker.launchCameraAsync()
- Expo.ImageManipulator.manipulate()
- Expo.Camera
<TouchableHighlight onPress={} />
- await Expo.Permissions.askAsync(Expo.Permissions.CAMERA_ROLL)
Lecture 9: Redux
- Facebook found that MVC architecture is too complex to scale.
- Facebook rearchitected into one-way data flow
- Flux: an application architecture for React utilizing a unidirectional data flow
- The views react to changes ins some number of “stores”
- The only thing that can update data in a store is a “dispatcher”
- The only way to trigger the dispatcher is by invoking “actions”
- Actions are triggered from views
- Redux: data management library inspired by Flux
- single source of truth for all the data
- the only way to update the state is by an action
- updates are made using pure functions - deterministic function that takes arguments and always returns the same thing for them and doesn’t have side-effects (no prints etc.)
- Action -> Store -> Views (which can trigger actions)
- Unlike in flux there is only a singular store
- simpleReact (example redux implementation)
- Reducer:
- takes the previous state and update and applies the update
- should be a pure function
- should be immutable - return a new object
- Store
- responsible for maintaining state
- exposes getter via
getState()
- can be updated by using
dispatch()
- Can add listeners that get invoked when state changes
- Action
- piece of data that contains all the information required to make a state update. Usually objects with a type key
- functions that create actions are called action creators
- actions must be dispatched
- Reducer:
npm install redux
const contacts = store.getState().contacts // in render()
// in AddContactScreen.js
store.dispatch(addContact({name: formState.name, phone: formState.phone}))
npm install react-redux
// in ContactListScreen.js
// only bind this component to contacts
// and this gives us this.props.contacts
const mapStateToProps = state => ({
contacts: state.contacts,
})
export default connect(mapStateToProps)(contactListScreen)
Lecture 10: Redux, Tools
- Review:
react-redux
<Provider />
gives children access to our redux storeconnect()
helps us subscribe to any subset of our store and bind our action creators
- Supporting Async Request:
Store.dispatch()
needs to accept other types
npm install isomorphic-fetch
- fetch implementation for node.js- Redux Middleware:
- Allows to extend redux without having to touch the implementation
- Any function with this prototype can be middleware:
({getState, dispatch}) => next => action => void
- Middleware is the chain by which we just can keep passing actions down the chain and modify it however we want.
- A
thunk
is a function what wraps an expression to delay its evaluation
React.Component.componentWillReceiveProps(nextProps)
- Persisting State
- redux-persist:
- abstracts out the storage of the store into AsyncStorage
- Gives us
persistStore
,persistReducer
,<PersistGate />
:- Automatically stores the state at every change
- Automatically rehydrates the store when the app is re-opened
- Will display loading screen while waiting for store to rehydrate
- redux-persist:
- Container vs. Presentational Components
- Redux helps apps scale, but does add complexity:
- Is the complexity worth it?
- Do as much as you can with local component state, then add redux if you hit pain points
- Tools:
- Babel - transpile all down to a language all browsers will understand
- @std/esm - use import/export statements in node
- Chrome devtools
- React/Redux devtools
- ESLint
npm install -g eslint
npx eslint --init
- local configeslint init
- global confignpx eslint fileToCheck.js
- kensho config
- Prettier
- integrates with ESLint through
--fix
option
- integrates with ESLint through
- Typescript - statically check types
- NPM
- to be able to do
npm run lint
addpackage.json
:
- to be able to do
"scripts": {
"lint": "eslint api.js screens/"
},
- npm trick:
npm update
npm install -g npm-check-updates
ncu -u