├── exercises
├── 4-lists-solution
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── Header.js
│ ├── ModifyObjectForm.js
│ ├── README.md
│ ├── Row.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ └── package.json
├── 4-lists
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── README.md
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ └── package.json
├── 5-debugging
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── README.md
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── bugs
│ │ ├── 1.js
│ │ ├── 2.js
│ │ ├── 3.js
│ │ └── 4.js
│ ├── package.json
│ └── solutions
│ │ ├── 1.js
│ │ ├── 2.js
│ │ ├── 3.js
│ │ └── 4.js
└── 6-navigation
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── App.js
│ ├── README.md
│ ├── app.json
│ ├── assets
│ ├── icon.png
│ └── splash.png
│ ├── bugs
│ ├── 1.js
│ ├── 2.js
│ ├── 3.js
│ └── 4.js
│ ├── package.json
│ └── solutions
│ ├── 1.js
│ ├── 2.js
│ ├── 3.js
│ └── 4.js
├── lecture
├── 0-js
│ ├── 0-syntax.js
│ ├── 1-types.js
│ ├── 2-objects.js
│ ├── 3-objectMutation.js
│ ├── 4-scopeVariables.js
│ └── 5-scopeFunctions.js
├── 1-js
│ ├── 0-closureBug.js
│ ├── 1-closureExample.js
│ ├── 2-iife.js
│ ├── 3-iifeClosure.js
│ ├── 4-hof.js
│ ├── 5-hang.js
│ ├── 6-stack.js
│ ├── 7-overflow.js
│ ├── 8-async.js
│ ├── 9-callbacks.js
│ ├── a-callbackAuth.js
│ ├── b-promises.js
│ ├── c-promiseAuth.js
│ ├── d-asyncAwaitAuth.js
│ ├── e-this.js
│ └── simple.html
├── 10-async-redux-tools
│ ├── .babelrc
│ ├── .eslintrc.yml
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── api.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── authServer
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package-lock.json
│ │ └── package.json
│ ├── contacts.js
│ ├── package-lock.json
│ ├── package.json
│ ├── redux
│ │ ├── actions.js
│ │ ├── reducer.js
│ │ └── store.js
│ ├── screens
│ │ ├── AddContactScreen.js
│ │ ├── ContactDetailsScreen.js
│ │ ├── ContactListScreen.js
│ │ ├── LoginScreen.js
│ │ └── SettingsScreen.js
│ └── simpleRedux
│ │ ├── reducer.js
│ │ ├── store.js
│ │ ├── store2.js
│ │ └── store3.js
├── 11-performance
│ ├── contacts
│ │ ├── .babelrc
│ │ ├── .eslintrc.yml
│ │ ├── .gitignore
│ │ ├── .watchmanconfig
│ │ ├── App.js
│ │ ├── FlatListContacts.js
│ │ ├── PureButton.js
│ │ ├── PureButtonScreen.js
│ │ ├── Row.js
│ │ ├── ScrollViewContacts.js
│ │ ├── SectionListContacts.js
│ │ ├── api.js
│ │ ├── app.json
│ │ ├── assets
│ │ │ ├── icon.png
│ │ │ └── splash.png
│ │ ├── authServer
│ │ │ ├── .gitignore
│ │ │ ├── README.md
│ │ │ ├── index.js
│ │ │ ├── package-lock.json
│ │ │ └── package.json
│ │ ├── contacts.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ ├── redux
│ │ │ ├── actions.js
│ │ │ ├── reducer.js
│ │ │ └── store.js
│ │ ├── screens
│ │ │ ├── AddContactScreen.js
│ │ │ ├── ContactDetailsScreen.js
│ │ │ ├── ContactListScreen.js
│ │ │ ├── LoginScreen.js
│ │ │ └── SettingsScreen.js
│ │ └── simpleRedux
│ │ │ ├── reducer.js
│ │ │ ├── store.js
│ │ │ ├── store2.js
│ │ │ └── store3.js
│ └── pomodoro-timer
│ │ ├── .babelrc
│ │ ├── .gitignore
│ │ ├── .watchmanconfig
│ │ ├── App.js
│ │ ├── ProgressBar.js
│ │ ├── ProgressBarAnimated.js
│ │ ├── app.json
│ │ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ │ ├── components
│ │ ├── Countdown.js
│ │ ├── TimeInput.js
│ │ ├── TimerToggleButton.js
│ │ └── index.js
│ │ ├── package-lock.json
│ │ ├── package.json
│ │ └── utils
│ │ ├── Timer.js
│ │ ├── index.js
│ │ └── vibrate.js
├── 12-deploying-testing
│ ├── .babelrc
│ ├── .eslintrc.yml
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── api.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── authServer
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package-lock.json
│ │ └── package.json
│ ├── components
│ │ ├── MyButton.js
│ │ ├── MyButton.test.js
│ │ └── __snapshots__
│ │ │ └── MyButton.test.js.snap
│ ├── contacts.js
│ ├── package-lock.json
│ ├── package.json
│ ├── redux
│ │ ├── __snapshots__
│ │ │ ├── actions.test.js.snap
│ │ │ └── reducer.test.js.snap
│ │ ├── actions.js
│ │ ├── actions.test.js
│ │ ├── reducer.js
│ │ ├── reducer.test.js
│ │ └── store.js
│ ├── screens
│ │ ├── AddContactScreen.js
│ │ ├── ContactDetailsScreen.js
│ │ ├── ContactListScreen.js
│ │ ├── LoginScreen.js
│ │ └── SettingsScreen.js
│ ├── simpleRedux
│ │ ├── reducer.js
│ │ ├── store.js
│ │ ├── store2.js
│ │ └── store3.js
│ └── testing
│ │ ├── sum.js
│ │ └── sum.test.js
├── 2-react
│ ├── 1-Set.js
│ ├── 2-Set.js
│ ├── 3-Todo.js
│ ├── 4-imperativeGuitar.js
│ ├── 5-declarativeGuitar.js
│ ├── 6-imperativeSlide.js
│ ├── 7-declarativeSlide.js
│ ├── 8-slideshow.html
│ ├── 9-slideshowComponents.js
│ ├── a-props.js
│ ├── b-state.js
│ ├── todoApp0.js
│ ├── todoApp1.js
│ ├── todoApp2.js
│ ├── todoApp3.js
│ └── todoApp4-react.js
├── 3-react-native
│ ├── 0-rnBlockJs.js
│ ├── 1-todoApp-rn.js
│ ├── 2-mount.js
│ ├── 3-update.js
│ ├── 4-unmount.js
│ └── 5-expo-app
│ │ ├── App.js
│ │ └── Count.js
├── 4-lists-input
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── contacts.js
│ └── package.json
├── 5-input-debugging
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── contacts.js
│ └── package.json
├── 6-navigation
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── contacts.js
│ ├── examples
│ │ ├── 0-Switch.js
│ │ └── 1-Stack.js
│ ├── package.json
│ └── screens
│ │ ├── AddContactScreen.js
│ │ ├── ContactDetailsScreen.js
│ │ ├── ContactListScreen.js
│ │ ├── LoginScreen.js
│ │ └── SettingsScreen.js
├── 7-data
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── api.js
│ ├── app.json
│ ├── assets
│ │ ├── icon.png
│ │ └── splash.png
│ ├── authServer
│ │ ├── .gitignore
│ │ ├── README.md
│ │ ├── index.js
│ │ ├── package-lock.json
│ │ └── package.json
│ ├── contacts.js
│ ├── package.json
│ └── screens
│ │ ├── AddContactScreen.js
│ │ ├── ContactDetailsScreen.js
│ │ ├── ContactListScreen.js
│ │ ├── LoginScreen.js
│ │ └── SettingsScreen.js
└── 9-redux
│ ├── .babelrc
│ ├── .gitignore
│ ├── .watchmanconfig
│ ├── AddContactForm.js
│ ├── App.js
│ ├── FlatListContacts.js
│ ├── Row.js
│ ├── ScrollViewContacts.js
│ ├── SectionListContacts.js
│ ├── api.js
│ ├── app.json
│ ├── assets
│ ├── icon.png
│ └── splash.png
│ ├── authServer
│ ├── .gitignore
│ ├── README.md
│ ├── index.js
│ ├── package-lock.json
│ └── package.json
│ ├── contacts.js
│ ├── package-lock.json
│ ├── package.json
│ ├── redux
│ ├── actions.js
│ ├── reducer.js
│ └── store.js
│ ├── screens
│ ├── AddContactScreen.js
│ ├── ContactDetailsScreen.js
│ ├── ContactListScreen.js
│ ├── LoginScreen.js
│ └── SettingsScreen.js
│ └── simpleRedux
│ ├── reducer.js
│ ├── store.js
│ └── store2.js
└── project0
└── solution
├── basic
├── index.html
├── script.js
└── styles.css
├── basic2
├── index.html
├── script.js
└── styles.css
└── withDelete2
├── index.html
├── script.js
└── styles.css
/exercises/4-lists-solution/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/Header.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {StyleSheet, Text, View} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | const styles = StyleSheet.create({
6 | container: {
7 | paddingLeft: 10,
8 | backgroundColor: '#aaa',
9 | },
10 | text: {
11 | fontWeight: 'bold',
12 | },
13 | })
14 |
15 | const Header = props => (
16 |
17 | {props.text}
18 |
19 | )
20 |
21 | Header.propTypes = {
22 | text: PropTypes.string,
23 | }
24 |
25 | export default Header
26 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/ModifyObjectForm.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Button, StyleSheet, TextInput, View} from 'react-native'
3 | import {Constants} from 'expo'
4 | import PropTypes from 'prop-types'
5 |
6 | const styles = StyleSheet.create({
7 | container: {
8 | flex: 1,
9 | backgroundColor: '#fff',
10 | paddingTop: Constants.statusBarHeight,
11 | },
12 | input: {
13 | borderWidth: 1,
14 | borderColor: 'black',
15 | minWidth: 100,
16 | marginTop: 20,
17 | marginHorizontal: 20,
18 | paddingHorizontal: 10,
19 | paddingVertical: 5,
20 | borderRadius: 3,
21 | },
22 | })
23 |
24 | export default class AddContactForm extends React.Component {
25 | state = {
26 | key: '',
27 | val: '',
28 | }
29 |
30 | handleKeyChange = key => {
31 | this.setState({key})
32 | }
33 |
34 | handleValChange = val => {
35 | this.setState({val})
36 | }
37 |
38 | handleSubmit = () => {
39 | this.props.onSubmit(this.state.key, this.state.val)
40 | }
41 |
42 | render() {
43 | return (
44 |
45 |
51 |
57 |
58 |
59 | )
60 | }
61 | }
62 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/README.md:
--------------------------------------------------------------------------------
1 | # Lists Solution
2 | In lecture we talked about scrolling views and a couple types of performant
3 | virtualized lists ([`FlatList`](https://facebook.github.io/react-native/docs/flatlist.html)
4 | and [`SectionList`](https://facebook.github.io/react-native/docs/sectionlist.html)).
5 |
6 | The goal of this exercise is to get used to the change in paradigm between normal
7 | views that render all of their children and these virtualized lists that take
8 | data and a renderer.
9 |
10 | ## Installation
11 | This exercise was bootstrapped using the [Expo XDE](https://docs.expo.io/versions/latest/introduction/xde-tour.html).
12 | To run it, open this directory as a project in the XDE.
13 |
14 | ## Exercise
15 | For this exercise, you will be writing a component that allows easy viewing of
16 | objects. This should be created using a `SectionList`, where the section headers
17 | are the object keys and the rows are the values. For values that are primitives,
18 | display them in a single row. For arrays, display multiple rows. No need to worry
19 | about any other types (objects, functions, etc.).
20 |
21 | ## Challenge
22 | If you want a challenge, try adding a view to add/modify these key/value pairs.
23 |
24 | - There should be two inputs, one for key and one for value
25 | - Adding a new key should add that key/value pair to the object
26 | - If the key exists and the value was a primitive, the new value should overwrite the old
27 | - If the key exists and the value was an array, the new value should be added to the beginning of the original array
28 |
29 | Hint: Use [`JSON.parse()`](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/JSON/parse)
30 | to parse strings into JS values.
31 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/Row.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {StyleSheet, Text, View} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | const styles = StyleSheet.create({
6 | row: {padding: 20},
7 | })
8 |
9 | const Row = props => (
10 |
11 | {props.value}
12 |
13 | )
14 |
15 | Row.propTypes = {
16 | value: PropTypes.any,
17 | }
18 |
19 | export default Row
20 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "4-lists",
4 | "description": "This project is really great.",
5 | "slug": "4-lists",
6 | "privacy": "public",
7 | "sdkVersion": "25.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "ios": {
18 | "supportsTablet": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/exercises/4-lists-solution/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/4-lists-solution/assets/icon.png
--------------------------------------------------------------------------------
/exercises/4-lists-solution/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/4-lists-solution/assets/splash.png
--------------------------------------------------------------------------------
/exercises/4-lists-solution/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "dependencies": {
5 | "expo": "^25.0.0",
6 | "react": "16.2.0",
7 | "react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/exercises/4-lists/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/exercises/4-lists/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/exercises/4-lists/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/exercises/4-lists/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | export default class App extends React.Component {
5 | render() {
6 | return (
7 |
8 | Open up App.js to start working on your app!
9 |
10 | );
11 | }
12 | }
13 |
14 | const styles = StyleSheet.create({
15 | container: {
16 | flex: 1,
17 | backgroundColor: '#fff',
18 | alignItems: 'center',
19 | justifyContent: 'center',
20 | },
21 | });
22 |
--------------------------------------------------------------------------------
/exercises/4-lists/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "4-lists",
4 | "description": "This project is really great.",
5 | "slug": "4-lists",
6 | "privacy": "public",
7 | "sdkVersion": "25.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "ios": {
18 | "supportsTablet": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/exercises/4-lists/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/4-lists/assets/icon.png
--------------------------------------------------------------------------------
/exercises/4-lists/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/4-lists/assets/splash.png
--------------------------------------------------------------------------------
/exercises/4-lists/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "dependencies": {
5 | "expo": "^25.0.0",
6 | "react": "16.2.0",
7 | "react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/exercises/5-debugging/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/exercises/5-debugging/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/exercises/5-debugging/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/exercises/5-debugging/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | import Bug from './bugs/1'
5 |
6 | export default class App extends React.Component {
7 | render() {
8 | return (
9 |
10 |
11 |
12 | );
13 | }
14 | }
15 |
16 | const styles = StyleSheet.create({
17 | container: {
18 | flex: 1,
19 | backgroundColor: '#fff',
20 | alignItems: 'center',
21 | justifyContent: 'center',
22 | },
23 | });
24 |
--------------------------------------------------------------------------------
/exercises/5-debugging/README.md:
--------------------------------------------------------------------------------
1 | # Lists
2 | In lecture we talked about debugging.
3 |
4 | The goal of this exercise is to practice the various debugging strategies that we
5 | discussed in lecture.
6 |
7 | ## Installation
8 | This exercise was bootstrapped using the [Expo XDE](https://docs.expo.io/versions/latest/introduction/xde-tour.html).
9 | To run it, open this directory as a project in the XDE.
10 |
11 | ## Exercise
12 | For this exercise, there are a few buggy files located in [/bugs](./bugs). Replace
13 | the import in [App.js](./App.js) with the buggy file to run it. Make sure not to
14 | change the lines that the files say not to change.
15 |
16 | ## Solution
17 | See the solution code in the [solutions directory](./solutions), or run them
18 | without looking at the code to play with the functionality.
19 |
--------------------------------------------------------------------------------
/exercises/5-debugging/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "5-debugging",
4 | "description": "This project is really great.",
5 | "slug": "5-debugging",
6 | "privacy": "public",
7 | "sdkVersion": "25.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "ios": {
18 | "supportsTablet": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/exercises/5-debugging/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/5-debugging/assets/icon.png
--------------------------------------------------------------------------------
/exercises/5-debugging/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/5-debugging/assets/splash.png
--------------------------------------------------------------------------------
/exercises/5-debugging/bugs/1.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {StyleSheet, Text, View} from 'react-native'
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | flexDirection: 'row',
8 | },
9 | center: {
10 | alignSelf: 'center',
11 | },
12 | greeting: {
13 | color: 'red',
14 | },
15 | })
16 |
17 | const Container = props => (
18 |
19 |
20 |
21 | )
22 |
23 | /////
24 | // Do not edit anything above this line
25 | /////
26 |
27 | const Greeting = props => (
28 |
29 | This text should be red and centered vertically
30 |
31 | )
32 |
33 | export default () =>
34 |
--------------------------------------------------------------------------------
/exercises/5-debugging/bugs/2.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Button, Text, View} from 'react-native'
3 |
4 | const shallowEqual = (obj1, obj2) => {
5 | const keys1 = Object.keys(obj1)
6 | const keys2 = Object.keys(obj2)
7 | if (keys1.length !== keys2.length) return false
8 | for (let i = 0; i < keys1.length; i++) {
9 | const key = keys1[i]
10 | if (obj1[key] !== obj2[key]) return false
11 | }
12 |
13 | return true
14 | }
15 |
16 | class ListText extends React.Component {
17 | shouldComponentUpdate(nextProps) {
18 | return !shallowEqual(nextProps, this.props)
19 | }
20 |
21 | render() {
22 | return (
23 |
24 | This list should add the next number to the list when button is pressed: {JSON.stringify(this.props.list)}
25 |
26 | )
27 | }
28 | }
29 |
30 |
31 | /////
32 | // Do not change anything above this line
33 | /////
34 |
35 | export default class Bug extends React.Component {
36 | state = {
37 | list: [],
38 | }
39 |
40 | addNumber() {
41 | const newList = this.state.list.push(this.state.list.length)
42 | this.setState({list: newList})
43 | }
44 |
45 | render() {
46 | return (
47 |
48 |
49 |
51 | )
52 | }
53 | }
54 |
--------------------------------------------------------------------------------
/exercises/5-debugging/bugs/3.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text, TextInput, StyleSheet } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | justifyContent: 'center',
7 | alignItems: 'center',
8 | padding: 10,
9 | },
10 | input: {
11 | borderColor: '#ddd',
12 | width: 100,
13 | borderWidth: 1,
14 | },
15 | messageContainer: {
16 | flexDirection: 'column',
17 | },
18 | messageHeader: {
19 | fontWeight: 'bold',
20 | },
21 | });
22 |
23 | export default class BugThree extends React.Component {
24 | state = {
25 | message: {
26 | owner: '',
27 | content: '',
28 | },
29 | }
30 |
31 | /////
32 | // Do not edit anything above this line
33 | /////
34 |
35 | handleOwnerChange = owner => {
36 | this.setState({ message: {owner}});
37 | }
38 |
39 | handleContentChange = content => {
40 | this.setState({ message: {content}});
41 | }
42 |
43 | render() {
44 | return (
45 |
46 |
51 |
56 |
57 | Current Message
58 | Owner: {this.state.message.owner}
59 | Content: {this.state.message.content}
60 |
61 |
62 | );
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/exercises/5-debugging/bugs/4.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, StyleSheet, Text, View } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | boldText: {
6 | fontWeight: 'bold',
7 | },
8 | })
9 |
10 | const BoldText = props => (
11 |
12 | {props.value}
13 |
14 | );
15 |
16 | /////
17 | // Do not edit anything above this line
18 | /////
19 |
20 | export default class Bug extends React.Component {
21 | greeting = {
22 | value: 'Hello, world!',
23 | valueChanged: false,
24 | }
25 |
26 | handleGreetingChange = () => {
27 | const newGreeting = 'Howdy, sir!'
28 | this.greeting = { value: newGreeting, valueChanged: true };
29 | }
30 |
31 | render() {
32 | return (
33 |
34 |
37 |
42 |
43 | );
44 | }
45 | }
46 |
47 |
--------------------------------------------------------------------------------
/exercises/5-debugging/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "dependencies": {
5 | "expo": "^25.0.0",
6 | "react": "16.2.0",
7 | "react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz"
8 | }
9 | }
10 |
--------------------------------------------------------------------------------
/exercises/5-debugging/solutions/1.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {StyleSheet, Text, View} from 'react-native'
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | flex: 1,
7 | flexDirection: 'row',
8 | },
9 | center: {
10 | alignSelf: 'center',
11 | },
12 | greeting: {
13 | color: 'red',
14 | },
15 | })
16 |
17 |
18 | const Container = props => (
19 |
20 |
21 |
22 | )
23 |
24 | /////
25 | // Do not edit anything above this line
26 | /////
27 |
28 | const Greeting = props => (
29 |
30 | This text should be red and centered vertically
31 |
32 | )
33 |
34 | export default () =>
35 |
--------------------------------------------------------------------------------
/exercises/5-debugging/solutions/2.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Button, Text, View} from 'react-native'
3 |
4 | const shallowEqual = (obj1, obj2) => {
5 | const keys1 = Object.keys(obj1)
6 | const keys2 = Object.keys(obj2)
7 | if (keys1.length !== keys2.length) return false
8 | for (let i = 0; i < keys1.length; i++) {
9 | const key = keys1[i]
10 | if (obj1[key] !== obj2[key]) return false
11 | }
12 |
13 | return true
14 | }
15 |
16 | class ListText extends React.Component {
17 | shouldComponentUpdate(nextProps) {
18 | return !shallowEqual(nextProps, this.props)
19 | }
20 |
21 | render() {
22 | return (
23 |
24 | This list should add the next number to the list when button is pressed: {JSON.stringify(this.props.list)}
25 |
26 | )
27 | }
28 | }
29 |
30 |
31 | /////
32 | // Do not change anything above this line
33 | /////
34 |
35 | export default class Bug extends React.Component {
36 | state = {
37 | list: [],
38 | }
39 |
40 | addNumber() {
41 | this.setState(prevState => ({list: [...prevState.list, prevState.list.length]}))
42 | }
43 |
44 | render() {
45 | return (
46 |
47 |
48 |
50 | )
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/exercises/5-debugging/solutions/3.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { View, Text, TextInput, StyleSheet } from 'react-native';
3 |
4 | const styles = StyleSheet.create({
5 | container: {
6 | justifyContent: 'center',
7 | alignItems: 'center',
8 | padding: 10,
9 | },
10 | input: {
11 | borderColor: '#ddd',
12 | width: 100,
13 | borderWidth: 1,
14 | },
15 | messageContainer: {
16 | flexDirection: 'column',
17 | },
18 | messageHeader: {
19 | fontWeight: 'bold',
20 | },
21 | });
22 |
23 | export default class BugThree extends React.Component {
24 | state = {
25 | message: {
26 | owner: '',
27 | content: '',
28 | },
29 | }
30 |
31 | render() {
32 | return (
33 |
34 |
39 |
44 |
45 | Current Message
46 | Owner: {this.state.message.owner}
47 | Content: {this.state.message.content}
48 |
49 |
50 | );
51 | }
52 |
53 | /////
54 | // Do not edit anything above this line
55 | /////
56 |
57 | _onOwnerChange = (owner) => {
58 | this.setState({ message: {...this.state.message, owner}});
59 | }
60 |
61 | _onContentChange = (content) => {
62 | this.setState({ message: {...this.state.message, content}});
63 | }
64 | }
65 |
--------------------------------------------------------------------------------
/exercises/5-debugging/solutions/4.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { Button, Text, View } from 'react-native';
3 |
4 | const BoldText = props => (
5 |
6 | {props.value}
7 |
8 | );
9 |
10 | /////
11 | // Do not edit anything above this line
12 | /////
13 |
14 | export default class Bug extends React.Component {
15 | state = {
16 | value: 'Hello, world!',
17 | valueChanged: false,
18 | }
19 |
20 | _changeGreeting = () => {
21 | const newGreeting = 'Howdy, sir!'
22 | this.setState({ value: newGreeting, valueChanged: true });
23 | }
24 |
25 | render() {
26 | return (
27 |
28 |
31 |
36 |
37 | );
38 | }
39 | }
40 |
--------------------------------------------------------------------------------
/exercises/6-navigation/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/exercises/6-navigation/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/exercises/6-navigation/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/exercises/6-navigation/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | import Bug from './bugs/1';
5 |
6 | export default class App extends React.Component {
7 | render() {
8 | return ;
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/exercises/6-navigation/README.md:
--------------------------------------------------------------------------------
1 | # Navigation
2 | In lecture we introduced navigation concepts and the react-navigation library.
3 |
4 | This exercise will walk though basic usage of the library, usage of route params, and demonstrate a common pitfall with nested navigators.
5 |
6 | ## Installation
7 | This exercise was bootstrapped using the [Expo XDE](https://docs.expo.io/versions/latest/introduction/xde-tour.html).
8 | To run it, open this directory as a project in the XDE.
9 |
10 | ## Exercise
11 | For this exercise, there are a few buggy files located in [/bugs](./bugs). Replace
12 | the import in [App.js](./App.js) with the buggy file to run it. Make sure not to
13 | change the lines that the files say not to change.
14 |
15 | ## Solution
16 | See the solution code in the [solutions directory](./solutions), or run them
17 | without looking at the code to play with the functionality.
18 |
--------------------------------------------------------------------------------
/exercises/6-navigation/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "5-debugging",
4 | "description": "This project is really great.",
5 | "slug": "5-debugging",
6 | "privacy": "public",
7 | "sdkVersion": "25.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "ios": {
18 | "supportsTablet": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/exercises/6-navigation/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/6-navigation/assets/icon.png
--------------------------------------------------------------------------------
/exercises/6-navigation/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/exercises/6-navigation/assets/splash.png
--------------------------------------------------------------------------------
/exercises/6-navigation/bugs/1.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | /////
16 | // Do not edit anything above this line
17 | /////
18 |
19 | const HomeScreen = props => (
20 |
21 | Home Screen
22 |
24 | );
25 |
26 | const ContactScreen = props => (
27 |
28 | Contact Screen
29 |
31 | );
32 |
33 | const AppNavigator = createStackNavigator({
34 | HomeScreen,
35 | ContactScreen,
36 | });
37 |
38 | export default AppNavigator;
39 |
--------------------------------------------------------------------------------
/exercises/6-navigation/bugs/2.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | const HomeScreen = ({ navigation }) => (
16 |
17 | Home Screen
18 |
25 | );
26 |
27 | /////
28 | // Do not edit anything above this line
29 | /////
30 |
31 | const ContactScreen = ({ navigation }) => (
32 |
33 | FIXME's Contact Screen
34 |
41 | );
42 |
43 | const AppNavigator = createStackNavigator({
44 | HomeScreen,
45 | ContactScreen,
46 | });
47 |
48 | export default AppNavigator;
49 |
--------------------------------------------------------------------------------
/exercises/6-navigation/bugs/4.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createTabNavigator, createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | const HomeScreen = ({ navigation }) => (
16 |
17 | Home Screen
18 |
25 | );
26 |
27 | const InfoScreen = ({ navigation }) => (
28 |
29 | Contact Info
30 |
31 | );
32 |
33 | const FriendsScreen = ({ navigation }) => (
34 |
35 | Friends
36 |
43 | );
44 |
45 | /////
46 | // Do not edit anything above this line
47 | /////
48 |
49 | const ContactNavigator = createTabNavigator({
50 | InfoScreen,
51 | FriendsScreen,
52 | });
53 |
54 | const ContactScreen = () => (
55 |
56 |
57 |
58 | );
59 |
60 | const AppNavigator = createStackNavigator({
61 | HomeScreen,
62 | ContactScreen,
63 | });
64 |
65 | export default AppNavigator;
66 |
--------------------------------------------------------------------------------
/exercises/6-navigation/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "dependencies": {
5 | "expo": "^25.0.0",
6 | "react": "16.2.0",
7 | "react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz",
8 | "react-navigation": "2.0.0-beta.5"
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/exercises/6-navigation/solutions/1.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | /////
16 | // Do not edit anything above this line
17 | /////
18 |
19 | const HomeScreen = ({ navigation }) => (
20 |
21 | Home Screen
22 |
29 | );
30 |
31 | const ContactScreen = ({ navigation }) => (
32 |
33 | Contact Screen
34 |
41 | );
42 |
43 | const AppNavigator = createStackNavigator({
44 | HomeScreen,
45 | ContactScreen,
46 | });
47 |
48 | export default AppNavigator;
49 |
--------------------------------------------------------------------------------
/exercises/6-navigation/solutions/2.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | const HomeScreen = ({ navigation }) => (
16 |
17 | Home Screen
18 |
25 | );
26 |
27 | /////
28 | // Do not edit anything above this line
29 | /////
30 |
31 | const ContactScreen = ({ navigation }) => (
32 |
33 |
34 | {navigation.getParam('name')}'s Contact Screen
35 |
36 |
43 | );
44 |
45 | const AppNavigator = createStackNavigator({
46 | HomeScreen,
47 | ContactScreen,
48 | });
49 |
50 | export default AppNavigator;
51 |
--------------------------------------------------------------------------------
/exercises/6-navigation/solutions/4.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Button, Text, View } from 'react-native';
3 | import { createTabNavigator, createStackNavigator } from 'react-navigation';
4 |
5 | const styles = StyleSheet.create({
6 | screen: {
7 | flex: 1,
8 | justifyContent: 'center',
9 | },
10 | label: {
11 | textAlign: 'center',
12 | },
13 | });
14 |
15 | const HomeScreen = ({ navigation }) => (
16 |
17 | Home Screen
18 |
25 | );
26 |
27 | const InfoScreen = ({ navigation }) => (
28 |
29 | Contact Info
30 |
31 | );
32 |
33 | const FriendsScreen = ({ navigation }) => (
34 |
35 | Friends
36 |
43 | );
44 |
45 | /////
46 | // Do not edit anything above this line
47 | /////
48 |
49 | // The reasoning for this solution is explained here:
50 | // https://v2.reactnavigation.org/docs/common-mistakes.html#explicitly-rendering-more-than-one-navigator
51 |
52 | const ContactScreen = createTabNavigator({
53 | InfoScreen,
54 | FriendsScreen,
55 | });
56 |
57 | const AppNavigator = createStackNavigator({
58 | HomeScreen,
59 | ContactScreen,
60 | });
61 |
62 | export default AppNavigator;
63 |
--------------------------------------------------------------------------------
/lecture/0-js/0-syntax.js:
--------------------------------------------------------------------------------
1 | // comments are prefixed with double slashes
2 | /*
3 | * Multi-line comments look like this
4 | */
5 |
6 | // camelCase is preferred
7 | // double-quotes create strings
8 | const firstName = "jordan";
9 |
10 | // semicolons are optional
11 | // single-quotes also create strings
12 | const lastName = 'Hayashi'
13 |
14 | // arrays can be declared inline
15 | // arrays can have multiple types (more on types later)
16 | const arr = [
17 | 'string',
18 | 42,
19 | function() { console.log('hi') },
20 | ]
21 |
22 | // this returns the element at the 2nd index and invokes it
23 | arr[2]()
24 |
25 | // this will iterate through the array and console log each element
26 | for (let i = 0; i < arr.length; i++) {
27 | console.log(arr[i])
28 | }
29 |
--------------------------------------------------------------------------------
/lecture/0-js/1-types.js:
--------------------------------------------------------------------------------
1 | const x = 42
2 |
3 | // get type by using "typeof"
4 | console.log(typeof x)
5 | console.log(typeof undefined)
6 |
7 | // this may surprise you...
8 | console.log(typeof null)
9 |
--------------------------------------------------------------------------------
/lecture/0-js/2-objects.js:
--------------------------------------------------------------------------------
1 |
2 | const o = new Object()
3 | o.firstName = 'Jordan'
4 | o.lastName = 'Hayashi'
5 | o.isTeaching = true
6 | o.greet = function() { console.log('Hello!') }
7 |
8 | console.log(JSON.stringify(o))
9 |
10 | const o2 = {}
11 | o2['firstName'] = 'Jordan'
12 | const a = 'lastName'
13 | o2[a] = 'Hayashi'
14 |
15 | const o3 = {
16 | firstName: 'Jordan',
17 | lastName: 'Hayashi',
18 | greet: function() {
19 | console.log('hi')
20 | },
21 | address: {
22 | street: "Main st.",
23 | number: '111'
24 | }
25 | }
26 |
27 | // see 3-objectsMutation.js for more objects
28 |
--------------------------------------------------------------------------------
/lecture/0-js/3-objectMutation.js:
--------------------------------------------------------------------------------
1 | const o = {
2 | a: 'a',
3 | b: 'b',
4 | obj: {
5 | key: 'key',
6 | },
7 | }
8 |
9 | const o2 = o
10 |
11 | o2.a = 'new value'
12 |
13 | // o and o2 reference the same object
14 | console.log(o.a)
15 |
16 | // this shallow-copies o into o3
17 | const o3 = Object.assign({}, o)
18 |
19 | // deep copy
20 | function deepCopy(obj) {
21 | // check if vals are objects
22 | // if so, copy that object (deep copy)
23 | // else return the value
24 | const keys = Object.keys(obj)
25 |
26 | const newObject = {}
27 |
28 | for (let i = 0; i < keys.length; i++) {
29 | const key = keys[i]
30 | if (typeof obj[key] === 'object') {
31 | newObject[key] = deepCopy(obj[key])
32 | } else {
33 | newObject[key] = obj[key]
34 | }
35 | }
36 |
37 | return newObject
38 | }
39 |
40 | const o4 = deepCopy(o)
41 |
42 | o.obj.key = 'new key!'
43 | console.log(o4.obj.key)
44 |
--------------------------------------------------------------------------------
/lecture/0-js/4-scopeVariables.js:
--------------------------------------------------------------------------------
1 | // "var" is lexically scoped, meaning it exists from time of declaration to end of func
2 | if (true) {
3 | var lexicallyScoped = 'This exists until the end of the function'
4 | }
5 |
6 | console.log(lexicallyScoped)
7 |
8 | // "let" and "const" are block scoped
9 | if (true) {
10 | let blockScoped = 'This exists until the next }'
11 | const alsoBlockScoped = 'As does this'
12 | }
13 |
14 | // this variable doesn't exist
15 | console.log(typeof blockScoped)
16 |
17 | thisIsAlsoAVariable = "hello"
18 |
19 | const thisIsAConst = 50
20 |
21 | // thisIsAConst++ // error!
22 |
23 | const constObj = {}
24 |
25 | // consts are still mutable
26 | constObj.a = 'a'
27 |
28 | let thisIsALet = 51
29 | thisIsALet = 50
30 |
31 | // let thisIsALet = 51 // errors!
32 |
33 | var thisIsAVar = 50
34 | thisIsAVar = 51
35 | var thisIsAVar = 'new value!'
36 |
--------------------------------------------------------------------------------
/lecture/0-js/5-scopeFunctions.js:
--------------------------------------------------------------------------------
1 | // functions are hoisted
2 | hoistedFunction()
3 |
4 | // but only if they are declared as functions and not as variables initialized to
5 | // anonymous functions
6 | console.log("typeof butNotThis: " + typeof butNotThis)
7 |
8 | function thisShouldWork() {
9 | console.log("functions are hoisted")
10 | }
11 |
12 | var butNotThis = function() {
13 | console.log("but variables aren't")
14 | }
15 |
--------------------------------------------------------------------------------
/lecture/1-js/0-closureBug.js:
--------------------------------------------------------------------------------
1 | function makeFunctionArray() {
2 | const arr = []
3 |
4 | for (var i = 0; i < 5; i++) {
5 | arr.push(function () { console.log(i) })
6 | }
7 |
8 | return arr
9 | }
10 |
11 | const functionArr = makeFunctionArray()
12 |
13 | // we expect this to log 0, but it doesn't
14 | functionArr[0]()
15 |
--------------------------------------------------------------------------------
/lecture/1-js/1-closureExample.js:
--------------------------------------------------------------------------------
1 | function makeHelloFunction() {
2 | var message = 'Hello!'
3 |
4 | function sayHello() {
5 | console.log(message)
6 | }
7 |
8 | return sayHello
9 | }
10 |
11 | const sayHello = makeHelloFunction()
12 |
13 | // the variable called message is not in scope here
14 | console.log('typeof message:', typeof message)
15 | // but the function sayHello still references a variable called message
16 | console.log(sayHello.toString())
17 |
18 | // because of the closure, sayHello still has access to the variables within scope
19 | // when it was declared
20 | sayHello()
21 |
--------------------------------------------------------------------------------
/lecture/1-js/2-iife.js:
--------------------------------------------------------------------------------
1 | // this creates the same closure as in 1-closureExample.js, but doesn't pollute
2 | // the global scope with a function called makeHelloFunction like that example
3 | const sayHello = (function () {
4 | var message = 'Hello!'
5 |
6 | function sayHello() {
7 | console.log(message)
8 | }
9 |
10 | return sayHello
11 | })()
12 |
13 | // IIFEs can also be used to create variables that are inaccessible from the global
14 | // scope
15 | const counter = (function() {
16 | let count = 0
17 |
18 | return {
19 | inc: function() { count = count + 1 },
20 | get: function() { console.log(count) },
21 | }
22 | })()
23 |
24 | counter.get()
25 | counter.inc()
26 | counter.get()
27 |
--------------------------------------------------------------------------------
/lecture/1-js/3-iifeClosure.js:
--------------------------------------------------------------------------------
1 | // we can create a closure around each anonymous function pushed to the array by
2 | // turning them into IIFEs
3 | function makeFunctionArray() {
4 | const arr = []
5 |
6 | for (var i = 0; i < 5; i++) {
7 | arr.push((function (x) {
8 | return function () { console.log(x) }
9 | })(i))
10 | }
11 |
12 | return arr
13 | }
14 |
15 | const functionArr = makeFunctionArray()
16 |
17 | // this now logs 0 as expected
18 | functionArr[0]()
19 |
--------------------------------------------------------------------------------
/lecture/1-js/4-hof.js:
--------------------------------------------------------------------------------
1 | // Higher Order Functions take funcs as args or return funcs
2 | function map(arr, fn) {
3 | const newArr = []
4 |
5 | arr.forEach(function(val) {
6 | newArr.push(fn(val))
7 | })
8 |
9 | return newArr
10 | }
11 |
12 | function addOne(num) { return num + 1 }
13 |
14 | const x = [0,1,2,3]
15 |
16 | console.log(map(x, addOne))
17 |
18 |
19 | function filter(arr, fn) {
20 | const newArr = []
21 | arr.forEach(val => {
22 | if (fn(val)) newArr.push(val)
23 | })
24 |
25 | return newArr
26 | }
27 |
28 | function reduce(arr, fn, initialVal) {
29 | let returnVal = initialVal
30 |
31 | arr.forEach(val => {
32 | returnVal = fn(returnVal, val)
33 | })
34 |
35 | return returnVal
36 | }
37 |
--------------------------------------------------------------------------------
/lecture/1-js/5-hang.js:
--------------------------------------------------------------------------------
1 | // this function will freeze a browser page if run in console
2 | function hang(seconds = 5) {
3 | const doneAt = Date.now() + seconds * 1000
4 | while(Date.now() < doneAt) {}
5 | }
6 |
--------------------------------------------------------------------------------
/lecture/1-js/6-stack.js:
--------------------------------------------------------------------------------
1 | // when errors are thrown, the entire callstack is logged
2 | function addOne(num) {
3 | throw new Error('oh no, an error!')
4 | }
5 |
6 | function getNum() {
7 | return addOne(10)
8 | }
9 |
10 | function c() {
11 | console.log(getNum() + getNum())
12 | }
13 |
14 | c()
15 |
--------------------------------------------------------------------------------
/lecture/1-js/7-overflow.js:
--------------------------------------------------------------------------------
1 | // this will recurse infinitely
2 | function recurse() {
3 | console.log('recursion!')
4 | return recurse()
5 | }
6 |
7 | // this wall cause a stack overflow
8 | recurse()
9 |
--------------------------------------------------------------------------------
/lecture/1-js/8-async.js:
--------------------------------------------------------------------------------
1 | function printOne() {
2 | console.log('one')
3 | }
4 |
5 | function printTwo() {
6 | console.log('two')
7 | }
8 |
9 | function printThree() {
10 | console.log('three')
11 | }
12 |
13 | // this may not print in the order that you expect, because of the way the JS
14 | // function queue works
15 | setTimeout(printOne, 1000)
16 | setTimeout(printTwo, 0)
17 | printThree()
18 |
--------------------------------------------------------------------------------
/lecture/1-js/9-callbacks.js:
--------------------------------------------------------------------------------
1 | // this is a HOF that invokes the function argument on 1
2 | function doSomethingWithOne(callback) {
3 | return callback(1)
4 | }
5 |
6 | doSomethingWithOne(console.log)
7 |
8 | // this is the same thing, but done asynchronously
9 | function doSomethingWithOneAsync(callback) {
10 | setTimeout(() => callback(1), 1000)
11 | }
12 |
13 | doSomethingWithOneAsync(console.log)
14 |
15 | // this simulates a database call that returns an object representing a person
16 | function getUserFromDatabase(callback) {
17 | // simulates getting data from db
18 | setTimeout(() => callback({firstName: 'Jordan', lastName: 'Hayashi'}), 1000)
19 | }
20 |
21 | // this is a function that greets a user, which we pass as a callback to getUserFromDatabase
22 | function greetUser(user) {
23 | console.log('Hi, ' + user.firstName)
24 | }
25 |
26 | getUserFromDatabase(greetUser)
27 |
--------------------------------------------------------------------------------
/lecture/1-js/a-callbackAuth.js:
--------------------------------------------------------------------------------
1 | // taken from a personal project of mine
2 | // https://github.com/jhhayashi/coupon-api/blob/master/controllers/auth.js
3 |
4 | function login(req, res, callback) {
5 | User.findOne({email: req.body.email}, function(err, user) {
6 | if (err) return callback(err)
7 |
8 | user.comparePassword(req.body.password, (err, isMatch) => {
9 | if (err) return callback(err)
10 | if (!isMatch) return res.status(401).send('Incorrect password')
11 |
12 | // add relevant data to token
13 | const payload = {id: user._id, email: user.email}
14 |
15 | jwt.sign(payload, config.secret, {}, function(err, token) {
16 | if (err) return callback(err)
17 |
18 | user.token = token
19 | user.save((err) => {
20 | if (err) return callback(err)
21 | res.json({token})
22 | })
23 | })
24 | })
25 | })
26 | }
27 |
--------------------------------------------------------------------------------
/lecture/1-js/b-promises.js:
--------------------------------------------------------------------------------
1 | // this doesn't actually do anything, it's just a demo of Promise syntax
2 |
3 | const url = ''
4 |
5 | fetch(url)
6 | .then(function(res) {
7 | return res.json()
8 | })
9 | .then(function(json) {
10 | return ({
11 | importantData: json.importantData,
12 | })
13 | })
14 | .then(function(data) {
15 | console.log(data)
16 | })
17 | .catch(function(err) {
18 | // handle error
19 | })
20 |
--------------------------------------------------------------------------------
/lecture/1-js/c-promiseAuth.js:
--------------------------------------------------------------------------------
1 | function login(req, res, callback) {
2 | User.findOne({email: req.body.email})
3 | .then(function(user) {
4 | return user.comparePassword(req.body.password)
5 | })
6 | .then(function(isMatch) {
7 | // have to throw in order to break Promise chain
8 | if (!isMatch) {
9 | res.status(401).send('Incorrect password')
10 | throw {earlyExit: true}
11 | }
12 | const payload = {id: user._id, email: user.email}
13 | return jwt.sign(payload, config.secret, {})
14 | })
15 | .then(function(token) {
16 | user.token = token
17 | return user.save()
18 | })
19 | .then(function() {
20 | res.json({token})
21 | })
22 | .catch(function(err) {
23 | if (!err.earlyExit) callback(err)
24 | })
25 | }
26 |
--------------------------------------------------------------------------------
/lecture/1-js/d-asyncAwaitAuth.js:
--------------------------------------------------------------------------------
1 | async function login(req, res, callback) {
2 | try {
3 | const user = await User.findOne({email: req.body.email})
4 | const isMatch = await user.comparePassword(req.body.password)
5 |
6 | if (!isMatch) return res.status(401).send('Incorrect password')
7 |
8 | const payload = {id: user._id, email: user.email}
9 | const token = await jwt.sign(payload, config.secret, {})
10 |
11 | user.token = token
12 | const success = await user.save()
13 |
14 | res.json({token})
15 | } catch (err) {
16 | callback(err)
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/lecture/1-js/e-this.js:
--------------------------------------------------------------------------------
1 | // NOTE: this doesn't work as a node script, since they are run as modules
2 | // `this` in this case is equal to module.exports, which is an empty object
3 | console.log(this)
4 |
5 | // this logs the global object
6 | function whatIsThis() {
7 | console.log(this)
8 | }
9 |
10 | whatIsThis()
11 |
12 | // =======================================
13 |
14 | const person = {
15 | name: 'Jordan',
16 | greet: function() { console.log('Hi, ' + this.name) }
17 | }
18 |
19 | person.greet() // Hi, Jordan
20 |
21 | // =====================================
22 |
23 | const friend = {
24 | name: 'David',
25 | }
26 |
27 | friend.greet = person.greet
28 |
29 | friend.greet() // Hi, david
30 |
31 | // ====================================
32 |
33 | const greetPerson = person.greet
34 |
35 | greetPerson() // Hi, undefined
36 |
37 | // make greetPerson() work, but not in node
38 | this.name = 'Global'
39 |
40 | // browser console or node REPL: Hi, Global
41 | // node script: Hi, undefined
42 | greetPerson()
43 |
44 | const reallyGreetPerson = person.greet.bind(person)
45 | reallyGreetPerson() // Hi, Jordan
46 |
47 | person.greet.call({name: 'Yowon'}) // Hi, Yowon
48 | person.greet.apply({name: 'Raylen'}) // Hi, Raylen
49 |
50 | // ====================================
51 |
52 | const newPerson = {
53 | name: 'Jordan',
54 | // arrow notation binds `this` lexically
55 | greet: () => console.log('Hi, ' + this.name)
56 | }
57 |
58 | newPerson.greet() // Hi, Global
59 |
60 | // bound functions cannot be bound again
61 | newPerson.greet.call(person) // Hi, Global
62 |
--------------------------------------------------------------------------------
/lecture/1-js/simple.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | This is a simple page
5 |
6 |
7 | This is the title
8 | And this is a paragraph
9 |
10 |
11 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/.eslintrc.yml:
--------------------------------------------------------------------------------
1 | extends: kensho
2 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/**/*
2 | .expo/*
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/FlatListContacts.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {FlatList} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | import Row from './Row'
6 |
7 | const renderItem = ({item}) =>
8 |
9 | const FlatListContacts = props =>
10 |
11 | FlatListContacts.propTypes = {
12 | contacts: PropTypes.array,
13 | }
14 |
15 | export default FlatListContacts
16 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/Row.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {TouchableOpacity, StyleSheet, Text, View} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | const styles = StyleSheet.create({
6 | row: {padding: 20},
7 | })
8 |
9 | const Row = props => (
10 | props.onSelectContact(props)}>
11 | {props.name}
12 | {props.phone}
13 |
14 | )
15 |
16 | Row.propTypes = {
17 | name: PropTypes.string,
18 | phone: PropTypes.string,
19 | }
20 |
21 | export default Row
22 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/ScrollViewContacts.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {ScrollView} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | import Row from './Row'
6 |
7 | const ScrollViewContacts = props => (
8 | {props.contacts.map(contact =>
)}
9 | )
10 |
11 | ScrollViewContacts.propTypes = {
12 | contacts: PropTypes.array,
13 | }
14 |
15 | export default ScrollViewContacts
16 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/SectionListContacts.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {SectionList, Text} from 'react-native'
3 | import PropTypes from 'prop-types'
4 |
5 | import Row from './Row'
6 |
7 | const renderSectionHeader = ({section}) => {section.title}
8 |
9 | const SectionListContacts = props => {
10 | const contactsByLetter = props.contacts.reduce((obj, contact) => {
11 | const firstLetter = contact.name[0].toUpperCase()
12 | return {
13 | ...obj,
14 | [firstLetter]: [...(obj[firstLetter] || []), contact],
15 | }
16 | }, {})
17 |
18 | const sections = Object.keys(contactsByLetter)
19 | .sort()
20 | .map(letter => ({
21 | data: contactsByLetter[letter],
22 | title: letter,
23 | }))
24 |
25 | return (
26 | item.phone}
28 | sections={sections}
29 | renderItem={({item}) =>
}
30 | renderSectionHeader={renderSectionHeader}
31 | />
32 | )
33 | }
34 |
35 | SectionListContacts.propTypes = {
36 | contacts: PropTypes.array,
37 | }
38 |
39 | export default SectionListContacts
40 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/api.js:
--------------------------------------------------------------------------------
1 | const processContact = contact => ({
2 | name: `${contact.name.first} ${contact.name.last}`,
3 | phone: contact.phone,
4 | })
5 |
6 | export const fetchUsers = async () => {
7 | const response = await fetch('https://randomuser.me/api/?results=50&nat=us')
8 | const {results} = await response.json()
9 | return results.map(processContact)
10 | }
11 |
12 | export const login = async (username, password) => {
13 | const response = await fetch('http://localhost:8000', {
14 | method: 'POST',
15 | headers: {'content-type': 'application/json'},
16 | body: JSON.stringify({username, password}),
17 | })
18 |
19 | if (response.ok) {
20 | const {token} = await response.json()
21 | return token
22 | }
23 |
24 | const errMessage = await response.text()
25 | throw new Error(errMessage)
26 | }
27 |
28 | export const poorlyFormatted = usedVar => usedVar
29 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "name": "list-examples",
4 | "description": "This project is really great.",
5 | "slug": "list-examples",
6 | "privacy": "public",
7 | "sdkVersion": "25.0.0",
8 | "platforms": ["ios", "android"],
9 | "version": "1.0.0",
10 | "orientation": "portrait",
11 | "icon": "./assets/icon.png",
12 | "splash": {
13 | "image": "./assets/splash.png",
14 | "resizeMode": "contain",
15 | "backgroundColor": "#ffffff"
16 | },
17 | "ios": {
18 | "supportsTablet": true
19 | }
20 | }
21 | }
22 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/assets/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/lecture/10-async-redux-tools/assets/icon.png
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/assets/splash.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/jhhayashi/react-native-course/011592e4253b56affe533c0d1a30dfc0bd394858/lecture/10-async-redux-tools/assets/splash.png
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/authServer/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/authServer/README.md:
--------------------------------------------------------------------------------
1 | # Mock Authentication Server
2 | This is a simple mock auth server. You can POST to any endpoint and it will act as a login.
3 |
4 | There is only one user with username: `username` and password: `password`. There is no
5 | way to add new users.
6 |
7 | ## Installation
8 | - Install dependencies with `npm install`
9 | - Run the server with `npm start`
10 | - Visit [http://localhost:8000](http://localhost:8000)
11 |
12 | You can optionally declare a `PORT` env variable to override the default port:
13 | - `PORT=12345 npm start`
14 | - Visit [http://localhost:12345](http://localhost:12345)
15 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/authServer/index.js:
--------------------------------------------------------------------------------
1 | const express = require('express')
2 | const bodyParser = require('body-parser')
3 |
4 | const PORT = process.env.PORT || 8000
5 |
6 | // usernames are keys and passwords are values
7 | const users = {
8 | username: 'password',
9 | }
10 |
11 | const app = express()
12 | app.use(bodyParser.json())
13 |
14 | app.post('*', (req, res) => {
15 | const {username, password} = req.body
16 |
17 | if (!username || !password) return res.status(400).send('Missing username or password')
18 | // in practice, this is potentially revealing too much information.
19 | // an attacker can probe the server to find all of the usernames.
20 | if (!users[username]) return res.status(403).send('User does not exist')
21 | if (users[username] !== password) return res.status(403).send('Incorrect password')
22 | return res.json({token: 'thisIsARealToken'})
23 | })
24 |
25 | // catch 404
26 | app.use((req, res, next) => {
27 | const err = new Error('Not Found')
28 | err.status = 404
29 | next(err)
30 | })
31 |
32 | app.use((err, req, res, next) => res.status(err.status || 500).send(err.message || 'There was a problem'))
33 |
34 | const server = app.listen(PORT)
35 | console.log(`Listening at http://localhost:${PORT}`)
36 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/authServer/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "authserver",
3 | "version": "1.0.0",
4 | "description": "Simple auth server for a demo",
5 | "main": "index.js",
6 | "scripts": {
7 | "start": "node index"
8 | },
9 | "author": "Jordan Hayashi",
10 | "license": "ISC",
11 | "dependencies": {
12 | "body-parser": "^1.18.2",
13 | "express": "^4.16.3"
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "main": "node_modules/expo/AppEntry.js",
3 | "private": true,
4 | "scripts": {
5 | "lint": "eslint api.js screens/"
6 | },
7 | "dependencies": {
8 | "expo": "^25.0.0",
9 | "isomorphic-fetch": "^2.2.1",
10 | "prop-types": "^15.6.1",
11 | "react": "16.2.0",
12 | "react-native": "https://github.com/expo/react-native/archive/sdk-25.0.0.tar.gz",
13 | "react-native-vector-icons": "^4.5.0",
14 | "react-navigation": "2.0.0-beta.5",
15 | "react-redux": "^5.0.7",
16 | "redux": "^3.7.2",
17 | "redux-persist": "^5.9.1",
18 | "redux-thunk": "^2.2.0"
19 | },
20 | "devDependencies": {
21 | "eslint": "^4.19.1",
22 | "eslint-config-kensho": "^4.0.1",
23 | "eslint-plugin-react": "^7.7.0",
24 | "prettier": "^1.12.0"
25 | }
26 | }
27 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/redux/actions.js:
--------------------------------------------------------------------------------
1 | import {login} from '../api'
2 |
3 | // action types
4 | export const UPDATE_USER = 'UPDATE_USER'
5 | export const UPDATE_CONTACT = 'UPDATE_CONTACT'
6 | export const LOG_IN_SENT = 'LOG_IN_SENT'
7 | export const LOG_IN_FULFILLED = 'LOG_IN_FULFILLED'
8 | export const LOG_IN_REJECTED = 'LOG_IN_REJECTED'
9 |
10 | // action creators
11 | export const updateUser = update => ({
12 | type: UPDATE_USER,
13 | payload: update,
14 | })
15 |
16 | export const addContact = newContact => ({
17 | type: UPDATE_CONTACT,
18 | payload: newContact,
19 | })
20 |
21 | // async action creator
22 | export const logInUser = (username, password) => async dispatch => {
23 | dispatch({type: LOG_IN_SENT})
24 | try {
25 | const token = await login(username, password)
26 | dispatch({type: LOG_IN_FULFILLED, payload: token})
27 | } catch (err) {
28 | dispatch({type: LOG_IN_REJECTED, payload: err.message})
29 | }
30 | }
31 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/redux/reducer.js:
--------------------------------------------------------------------------------
1 | import {combineReducers} from 'redux'
2 |
3 | import {UPDATE_USER, UPDATE_CONTACT, LOG_IN_SENT, LOG_IN_FULFILLED, LOG_IN_REJECTED} from './actions'
4 |
5 | const merge = (prev, next) => Object.assign({}, prev, next)
6 |
7 | const contactReducer = (state = [], action) => {
8 | if (action.type === UPDATE_CONTACT) return [...state, action.payload]
9 | return state
10 | }
11 |
12 | const userReducer = (state = {}, action) => {
13 | switch (action.type) {
14 | case UPDATE_USER:
15 | return merge(state, action.payload)
16 | case UPDATE_CONTACT:
17 | return merge(state, {prevContact: action.payload})
18 | case LOG_IN_FULFILLED:
19 | return merge(state, {token: action.payload})
20 | case LOG_IN_REJECTED:
21 | return merge(state, {loginErr: action.payload})
22 | default:
23 | return state
24 | }
25 | }
26 |
27 | const reducer = combineReducers({
28 | user: userReducer,
29 | contacts: contactReducer,
30 | })
31 |
32 | export default reducer
33 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/redux/store.js:
--------------------------------------------------------------------------------
1 | import {createStore, applyMiddleware} from 'redux'
2 | import thunk from 'redux-thunk'
3 | import { persistStore, persistReducer } from 'redux-persist'
4 | import storage from 'redux-persist/lib/storage'
5 |
6 | import {addContact} from './actions'
7 | import reducer from './reducer'
8 |
9 | const persistConfig = {
10 | key: 'root',
11 | storage,
12 | }
13 |
14 | const persistedReducer = persistReducer(persistConfig, reducer)
15 |
16 | /*
17 | const thunkMiddleware = store => next => action => {
18 | if (typeof action === 'function') {
19 | action(store.dispatch)
20 | } else {
21 | next(action)
22 | }
23 | }
24 | */
25 |
26 | export const store = createStore(persistedReducer, applyMiddleware(thunk))
27 | export const persistor = persistStore(store)
28 |
29 | /*
30 | store.dispatch(updateUser({foo: 'foo'}))
31 | store.dispatch(updateUser({bar: 'bar'}))
32 | store.dispatch(updateUser({foo: 'baz'}))
33 |
34 | store.dispatch(addContact({name: 'jordan h', phone: '1234567890'}))
35 | store.dispatch(addContact({name: 'jordan h', phone: '1234567890'}))
36 | store.dispatch(addContact({name: 'david m', phone: '5050505050'}))
37 |
38 | console.log(store.getState())
39 | */
40 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/screens/AddContactScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import AddContactForm from '../AddContactForm'
3 | import {connect} from 'react-redux'
4 |
5 | import {addContact} from '../redux/actions'
6 |
7 | class AddContactScreen extends React.Component {
8 | static navigationOptions = {
9 | headerTitle: 'New Contact',
10 | }
11 |
12 | handleSubmit = formState => {
13 | this.props.addContact({name: formState.name, phone: formState.phone})
14 | this.props.navigation.navigate('ContactList')
15 | }
16 |
17 | render() {
18 | return
19 | }
20 | }
21 |
22 | export default connect(null, {addContact: addContact})(AddContactScreen)
23 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/screens/ContactDetailsScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Button, Text, View} from 'react-native'
3 |
4 | export default class ContactDetailsScreen extends React.Component {
5 | static navigationOptions = ({navigation}) => ({
6 | headerTitle: navigation.getParam('name'),
7 | })
8 |
9 | render() {
10 | return (
11 |
12 | {this.props.navigation.getParam('phone')}
13 |
14 |
15 | )
16 | }
17 |
18 | goToRandomContact = () => {
19 | const {contacts} = this.props.screenProps
20 | const phone = this.props.navigation.getParam('phone')
21 | let randomContact
22 | while (!randomContact) {
23 | const randomIndex = Math.floor(Math.random() * contacts.length)
24 | if (contacts[randomIndex].phone !== phone) {
25 | randomContact = contacts[randomIndex]
26 | }
27 | }
28 |
29 | // this.props.navigation.navigate('ContactDetails', {
30 | // ...randomContact,
31 | // });
32 | this.props.navigation.push('ContactDetails', {
33 | ...randomContact,
34 | })
35 | }
36 | }
37 |
--------------------------------------------------------------------------------
/lecture/10-async-redux-tools/screens/ContactListScreen.js:
--------------------------------------------------------------------------------
1 | import React from 'react'
2 | import {Button, View, StyleSheet} from 'react-native'
3 | import {connect} from 'react-redux'
4 |
5 | import SectionListContacts from '../SectionListContacts'
6 |
7 | class ContactListScreen extends React.Component {
8 | static navigationOptions = ({navigation}) => ({
9 | headerTitle: 'Contacts',
10 | headerRight: (
11 |