CS50 React Native

Table of Contents

Youtube

Lecture 0: JS

slides

  • 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() OR const o2 = {}; o2.firstName = '' OR const 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.
  • 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
  • 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
  • Closures
    • functions that refer to variables declared by parent function
    • possible because of scoping

Lecture 1: JS, ES6

slides

  • 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
  • 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

slides

  • 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
  • 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
  • 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 of arr
  • React paradigm: if you ever change something, replace it with something new.

Lecture 3: React Native

slides

  • 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
  • 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()
  • 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
  • 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
    • 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
  • 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
// 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

slides

  • 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
  • SectionList
    • Like FlatList with additional support for sections
    • Instead of data prop, define sections
      • Each section has it’s own data array
      • Each section can override the renderItem function
    • Pass a renderSectionHeader function for section headers
  • 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 />: Pass value and onChangeText props

Lecture 5: User Input, Debugging

slides

  • 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 after setState()
  • 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() or throw 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
  • 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

slides

  • yarn add react-navigation
  • SwitchNavigator: function that returns a component to be returned in App.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 of navigate() 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 to initialRouteName)
  • TabNavigator
    • The state of inactive screens is maintained
    • Use icons instead of only labels: yarn add react-native-vector-icons

Lecture 7: Data

slides

  • https://randomuser.me
  • fetch(url, config) is polyfiled. config is optional. Returns a promise.
  • Promises
    • .then()/.catch()
  • Async/Await: within an async function we can await the value of another async function or Promise.
    • Use try/catch to handle errors.
  • 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
  • JSX: TAG parameter={true} shortcut is parameter

Lecture 8: Expo Components

slides

  • 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'
    • Expo.ImagePicker.launchImageLibraryAsync()
    • Expo.ImagePicker.launchCameraAsync()
    • Expo.ImageManipulator.manipulate()
    • Expo.Camera
    • <TouchableHighlight onPress={} />

Lecture 9: Redux

slides

  • 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
  • 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

slides

  • Review: react-redux
    • <Provider /> gives children access to our redux store
    • connect() 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
  • 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 config
      • eslint init - global config
      • npx eslint fileToCheck.js
      • kensho config
    • Prettier
      • integrates with ESLint through --fix option
    • Typescript - statically check types
    • NPM
      • to be able to do npm run lint add package.json:
  "scripts": {
    "lint": "eslint api.js screens/"
  },
  • npm trick:
npm update
npm install -g npm-check-updates
ncu -u