├── InstallingNode.md
├── README.md
├── Slides.pdf
├── Steps
├── App-CRNA-Starter-Template.js
├── App-CRNA-StarterTemplate.png
├── App-Final-Detail.png
├── App-Final-Master.png
├── App-Step4-LiveReloading.js
├── App-Step4-LiveReloading.png
├── App-Step5-CustomComponents.js
├── App-Step5-CustomComponents.png
├── App-Step6-ListItem.js
├── App-Step6-ListItem.png
├── App-Step7-ScrollView.js
├── App-Step7-ScrollView.png
├── App-Step8-Networking.js
├── App-Step8-Networking.png
├── App-Step9-Navigation-Detail.png
├── App-Step9-Navigation-Master.png
└── App-Step9-Navigation.js
├── WeatherApp.gif
├── WeatherApp
├── .DS_Store
├── .babelrc
├── .flowconfig
├── .gitignore
├── .watchmanconfig
├── App.js
├── App.test.js
├── README.md
├── __snapshots__
│ └── App.test.js.snap
├── app.json
└── package.json
└── city.list.json
/InstallingNode.md:
--------------------------------------------------------------------------------
1 | # Installing Node
2 |
3 | There's more than one way to install Node, but we think the following instructions provide the best user experience.
4 |
5 | ## MacOS
6 |
7 | We recommend installing Node and Watchman using Homebrew: http://brew.sh/
8 | Install Homebrew, then run the following command in a Terminal:
9 |
10 | brew install node
11 |
12 | ## Windows 10
13 |
14 | We recommend installing Node using Chocolatey, a popular package manager for Windows: https://chocolatey.org/
15 |
16 | Install Chocolatey, then open a Command Prompt as Administrator and run the following command:
17 |
18 | choco install nodejs.install
19 |
20 | ## Linux
21 |
22 | Follow the installation instructions for your Linux distribution to install Node 6 or newer: https://nodejs.org/en/download/package-manager/
23 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Native 101 Class
2 |
3 | Welcome to React Native 101! In this class we will learn more about React Native and how it can be used to build a simple mobile app.
4 |
5 | # What is React Native?
6 |
7 | React Native is a framework for building native applications using React. React Native apps are written in JavaScript and can target iOS and Android devices.
8 |
9 | - You will be writing mostly JavaScript code. Your code along with any dependencies is then packaged into a single JavaScript file which is then bundled with the native app.
10 |
11 | - During development, we will be downloading the JS bundle from our dev machine and into the native app, as this will allow us to quickly iterate on the app.
12 |
13 | - Your JavaScript code will be executed in a dedicated JavaScript thread, which will communicate with the main UI thread via a bridge.
14 | This is a two-way street, meaning that events from the native side (such as touches and gestures) are also sent back to the JS thread.
15 |
16 | - You can still write native code, if needed.
17 |
18 | > You can learn more about React Native at http://facebook.github.io/react-native/
19 |
20 | # Prerequisites
21 |
22 | This class assumes some basic knowledge of modern JavaScript. React Native supports most ES6 language features, but for simplicity we will try to stick to ES5 whenever possible.
23 |
24 | - [Node v6](./InstallingNode.md) is required.
25 |
26 | - You should also install the Expo app on your device. This will make it easier for you to preview your app. You can download the Expo app here: https://expo.io
27 |
28 | - Finally, join our WiFi network, "FBOpenSource". The password is "opensource".
29 |
30 | # Building an app using React Native
31 |
32 | Today we will be building a simple weather app using React Native.
33 |
34 | The weather app will consist of two screens:
35 |
36 | - The first screen will display a list of cities. Each item in the list will display the name of the city, and some basic weather information (temperature, conditions).
37 |
38 | - The second screen will display additional details such as humidity, wind speed, and minimum/maximum temperatures for the day.
39 |
40 | The app will look something like this when finished:
41 |
42 | 
43 |
44 | > Try it out on Snack! https://snack.expo.io/rkBtI7BAg
45 |
46 | The app will use current weather data provided by the [Open Weather Map API](https://openweathermap.org/api).
47 |
48 | At the end of this class, you will have learned the following skills:
49 |
50 | - Setup and configuration of a basic React Native app
51 | - How to run a React Native app on a device
52 | - Writing reusable, customizable React components
53 | - Composing multiple React components to build complex interfaces
54 | - Building lists in React Native
55 | - Fetching data from remote endpoints
56 | - Navigating between multiple screens
57 |
58 | ## Using this document
59 |
60 | We will be covering a lot of ground today. Your instructor will copy and paste code from this document into the app throughout the class. You can tell which step we're at by looking at the comments that get pasted along with the code.
61 |
62 |
65 |
66 | I'll ask you to run a command at various points in my class. These commands can be run in a Terminal (MacOS), Command Prompt (Windows), or shell (Linux).
67 |
68 | The [`WeatherApp` directory](https://github.com/hramos/ReactNativeClass/tree/master/WeatherApp) in this repo contains the final app code. Feel free to refer to it if you get lost or you just want to see the final app we're working towards.
69 |
70 | If you want to follow along with the instructor, from scratch, then read on for a detailed list of steps.
71 |
72 | ## Table of Contents
73 |
74 | - Step 1: Install "Create React Native App"
75 | - Step 2: Create a new React Native app
76 | - Step 3: Run your app on a device
77 | - Step 4: Experiment with Live Reloading
78 | - Step 5: Create a custom component
79 | - Step 6: Installing packages from npm
80 | - Step 7: Build a scrollable list
81 | - Step 8: Fetching data from the network
82 | - Step 9: Navigating between screens
83 | - Homework
84 |
85 | ---
86 |
87 | # Step 1: Install "Create React Native App"
88 |
89 | Run the following command:
90 |
91 | ```bash
92 | npm install -g create-react-native-app
93 | ```
94 |
95 |
96 | ---
97 |
98 | # Step 2: Create a new React Native app
99 |
100 | Run the following commands:
101 |
102 | ```bash
103 | create-react-native-app MyWeatherApp
104 | ```
105 |
106 | At this point, the create-react-native-app (CRNA) command line utility will create a basic directory structure for your application and it will also download any dependencies.
107 |
108 |
109 | ---
110 |
111 | # Step 3: Run your app on a device
112 |
113 | Once CRNA has finished setting up your app, navigate to the newly created folder:
114 |
115 | ```bash
116 | cd MyWeatherApp
117 | ```
118 |
119 | Then run the following command to start the packager:
120 |
121 | ```bash
122 | npm start
123 | ```
124 |
125 | Now, open the Expo app on your phone and scan the QR code that should be visible on your screen. You should now see a very basic app on your phone. Congrats, you're now running your very first React Native app on an actual device!
126 |
127 | > Tip: You can scan your instructor's QR code, too. This way, you can run the instructor's app on your device and lay back as they build the app for you.
128 |
129 | 
130 |
131 | > Refer to the README.md file created by CRNA for more information on all the available commands.
132 |
133 |
134 | ---
135 |
136 | # Step 4: Experiment with Live Reloading
137 |
138 | The app that is currently running on your device is fully native. What you're seeing now on screen is made up entirely of native UI components. As you now know, this UI is controlled by JavaScript code. That means that we can, at any point, refresh and get the latest version of our JavaScript code. And we can do this without having to compile and install a new version of our app on the device.
139 |
140 | Before we make any changes, let's take a minute to understand how our (very basic) interface is defined right now.
141 |
142 | Open `App.js` and look for the `render()` method inside the `App` class. It looks something like this:
143 |
144 | ```javascript
145 | render() {
146 | return (
147 |
148 | Open up App.js to start working on your app!
149 | Changes you make will automatically reload.
150 | Shake your phone to open the developer menu.
151 |
152 | );
153 | }
154 | ```
155 |
156 | Here we can see that our component is rendering a `View` component that contains three `Text` components.
157 |
158 | A `View` is a built-in React Native component that works much like a `div` in the web. It maps to a `UIView` on iOS, or `android.view` on Android. The `View` component will serve as a building block for most of your React Native components.
159 |
160 | A `View` can be styled. In the example above, we are using a style that has been defined at the bottom of our `App.js` file using the `StyleSheet` API:
161 |
162 | ```javascript
163 | const styles = StyleSheet.create({
164 | container: {
165 | flex: 1,
166 | backgroundColor: '#fff',
167 | alignItems: 'center',
168 | justifyContent: 'center',
169 | }
170 | });
171 | ```
172 |
173 | The style names and values usually match how CSS works on the web, except names are written using camel casing, e.g `backgroundColor` rather than `background-color`. The style prop can also be a plain old JavaScript object, as we'll see below.
174 |
175 | The `Text` component allows you to display text. It can be styled, much like a `span` in the web.
176 |
177 | Go ahead and try adding some styles to one of our `Text` components:
178 |
179 | ```javascript
180 |
181 | Changes you make will automatically reload.
182 |
183 | ```
184 |
185 | Save your changes, and watch as your app automatically updates itself.
186 |
187 | 
188 |
189 | > Code for Step 4 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step4-LiveReloading.js).
190 |
191 | > Learn more about styles here: https://facebook.github.io/react-native/docs/style.html
192 |
193 |
194 | ---
195 |
196 | # Step 5: Create a custom component
197 |
198 | In order to build a more complex app, we'll need to go beyond the simple default `App` component. This being a React application, we can progressively create new components that can be reused across our application.
199 |
200 | The application we're building consists mainly of a vertical list of cities. Each item in the list will display the name of a city, as well as some basic weather data for that city. We can start building our application by creating a simple component that will display the data for a given city.
201 |
202 | Let's name this new component, "CityWeather":
203 |
204 | ```javascript
205 | class CityWeather extends React.Component {
206 | render() {
207 | return (
208 |
215 |
216 | City
217 |
218 |
219 | Weather
220 |
221 |
222 | Temp
223 |
224 |
225 | );
226 | }
227 | }
228 | ```
229 |
230 | You can add this above your `App` definition. You can now update your `App` to render this new component:
231 |
232 | ```javascript
233 | class App extends React.Component {
234 | render() {
235 | return (
236 |
237 |
238 |
239 | );
240 | }
241 | }
242 | ```
243 |
244 | You should now see a simple text box on screen that says "City Weather Temp" after saving. It does not look like much, but this will eventually show the data we want for each city.
245 |
246 | ## Using "props"
247 |
248 | We now have a component that we can use anywhere in our app, but it's not very reusable because it always displays the same bit of text. To make our component truly customizable, we will want to pass along some parameters to let it know what text to display.
249 |
250 | Consider an updated `CityWeather` component that can take a handful of parameters such as "name" and "weather":
251 |
252 | ```javascript
253 |
258 | ```
259 |
260 | These parameters are called "props", and can be accessed via `this.props`. Our updated and fully customizable `CityWeather` component would now look like this:
261 |
262 | ```javascript
263 | class CityWeather extends React.Component {
264 | render() {
265 | return (
266 |
273 |
274 | {this.props.name}
275 |
276 |
277 | {this.props.weather}
278 |
279 |
280 | {this.props.temp} °F
281 |
282 |
283 | );
284 | }
285 | }
286 | ```
287 |
288 | 
289 |
290 | > Code for Step 5 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step5-CustomComponents.js).
291 |
292 |
293 | > Want to learn more about props? Visit https://facebook.github.io/react-native/docs/props.html
294 |
295 |
296 | ---
297 |
298 | # Step 6: Installing packages from npm
299 |
300 | We now have a custom component we can use to display our city weather, but it's not looking as nice as I'd like it to be. It's missing some basic components such as the chevron on the right and a small weather image on the left. The strings also need to be positioned correctly if we want them to match what our final app is supposed to look like.
301 |
302 | There's a third party library of components called `react-native-elements` that has a `ListItem` component that looks pretty nice. I'll need to add the `react-native-elements` package to my project before I can use the `ListItem` component:
303 |
304 | Run the following command:
305 |
306 | ```bash
307 | npm install --save react-native-elements
308 | ```
309 |
310 | Now go back to `App.js` and import the `ListItem` component by adding the following to your list of imports at the top:
311 |
312 | ```javascript
313 | import { ListItem } from 'react-native-elements';
314 | ```
315 |
316 | Let's update our `CityWeather` component so that it renders a `ListItem` instead:
317 |
318 | ```javascript
319 | class CityWeather extends React.Component {
320 | render() {
321 | return (
322 |
338 | );
339 | }
340 | }
341 | ```
342 |
343 | Save and notice how our component is now rendering the strings in the right positions.
344 |
345 | 
346 |
347 | > Code for Step 6 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step6-ListItem.js).
348 |
349 |
350 | ---
351 |
352 | # Step 7: Build a scrollable list
353 |
354 | Now we have a component that can display weather data for a city. Now we just need to display a list with multiple copies of this component.
355 |
356 | We will be using our component's internal state in order to track which cities we'd like to display. We can initialize our state in the `constructor()` for our component:
357 |
358 | ```javascript
359 | constructor() {
360 | super();
361 |
362 | this.state = {
363 | cities: [
364 | { name: 'San Jose', id: 5392171 },
365 | { name: 'New York', id: 5128581 },
366 | { name: 'London', id: 2643744 },
367 | { name: 'Paris', id: 2968815 },
368 | { name: 'Hong Kong', id: 1819729 },
369 | { name: 'Singapore', id: 1880252 },
370 | { name: 'Beijing', id: 1816670 },
371 | { name: 'Sydney', id: 6619279 },
372 | { name: 'São Paulo', id: 3448439 },
373 | { name: 'San Juan', id: 4568138 },
374 | { name: 'Mumbai', id: 1275339 },
375 | { name: 'Reykjavík', id: 6692263 },
376 | ],
377 | };
378 | }
379 | ```
380 |
381 | > If you want to add a new city, refer to the `city.list.json` file in this repository to get the id for the city you're interested in.
382 |
383 | Then, we can update the `render()` method so that it displays a new `CityWeather` component for each of the entries in the `cities` array in our state:
384 |
385 | ```javascript
386 | render() {
387 | return (
388 |
389 | {this.state.cities.map(city => (
390 |
391 | ))}
392 |
393 | );
394 | }
395 | ```
396 |
397 | The updated `App` component should now look like this:
398 |
399 | ```javascript
400 | export default class App extends React.Component {
401 | constructor() {
402 | super();
403 |
404 | this.state = {
405 | cities: [
406 | { name: 'San Jose', id: 5392171 },
407 | { name: 'New York', id: 5128581 },
408 | { name: 'London', id: 2643744 },
409 | { name: 'Paris', id: 2968815 },
410 | { name: 'Hong Kong', id: 1819729 },
411 | { name: 'Singapore', id: 1880252 },
412 | { name: 'Beijing', id: 1816670 },
413 | { name: 'Sydney', id: 6619279 },
414 | { name: 'São Paulo', id: 3448439 },
415 | { name: 'San Juan', id: 4568138 },
416 | { name: 'Mumbai', id: 1275339 },
417 | { name: 'Reykjavík', id: 6692263 },
418 | ],
419 | };
420 | }
421 |
422 | render() {
423 | return (
424 |
425 | {this.state.cities.map(city => (
426 |
427 | ))}
428 |
429 | );
430 | }
431 | }
432 | ```
433 |
434 | Your app will now display multiple `CityWeather` components in a vertical fashion. It's looking more like a list now, but you'll soon spot a problem with our app. The last element in the list is off-screen! Our list needs to be scrollable so the user can access all of the content.
435 |
436 | In React Native, we can build a scrollable list of items using the `ScrollView` component. First, you will need to update the import at the top of the file to ensure we can use `ScrollView` in our app. Add `ScrollView` to the list of imports:
437 |
438 | ```javascript
439 | import {
440 | ScrollView,
441 | StyleSheet,
442 | Text,
443 | View,
444 | } from 'react-native';
445 | ```
446 |
447 | You can now replace the `View` component in our app with a `ScrollView`:
448 |
449 | ```javascript
450 | export default class App extends React.Component {
451 | render() {
452 | return (
453 |
454 | {this.state.cities.map(city => (
455 |
456 | ))}
457 |
458 | );
459 | }
460 | }
461 | ```
462 |
463 | You should now be able to scroll up and down through the list of cities.
464 |
465 | 
466 |
467 | > Code for Step 7 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step7-ScrollView.js).
468 |
469 |
470 | ---
471 |
472 | # Step 8: Fetching data from the network
473 |
474 | It's time to make our app a bit more dynamic. Imagine there is a weather API we can query in order to get up to date weather information for each of our cities. This API will respond to properly crafted HTTP GET requests with a JSON string. We'll need a way to make these HTTP requests.
475 |
476 | ## Using the Fetch API
477 |
478 | You can make network requests in React Native using `fetch()`. A basic `fetch()` call looks like this:
479 |
480 | ```javascript
481 | fetch('http://www.someapi.com/data.json')
482 | .then((res) => {
483 | // process response
484 | })
485 | .catch((err) => {
486 | // handle error
487 | })
488 | ```
489 |
490 | Given a URL, `fetch()` will make a GET request, and return a Promise object that will either resolve with a response, or reject and provide an error.
491 |
492 | Now all we need is a weather API to query.
493 |
494 | ## The Open Weather Map API
495 |
496 | We can use the Open Weather Map API to grab some basic weather information for each of our cities. There's an endpoint we can use to request weather data for cities in bulk. The URL we will query looks something like this:
497 |
498 | http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=YOUR_APP_ID&id=CITY_IDS
499 |
500 | This is basically a GET request to the endpoint at 'http://api.openweathermap.org/data/2.5/group', with some additional parameters:
501 |
502 | - 'APPID' is a key that identifies the app and authorizes use of the API. Your instructor will be using 'b1b35bba8b434a28a0be2a3e1071ae5b' as the app id, and you can get your own by signing up at http://home.openweathermap.org/users/sign_up
503 |
504 | - 'units' allows us to specify we want the response in imperial units (degrees Fahrenheit). You can use metric if you prefer.
505 |
506 | - 'ids' is a comma separated list of city ids.
507 |
508 | Using `fetch()`, the code would look something like this:
509 |
510 | ```javascript
511 | fetch('http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=1689498,5128638,2643744,2968815,1819729,1880252,1816670,6619279,3448439')
512 | .then((res) => console.log(res))
513 | .catch((err) => console.error(err));
514 | ```
515 |
516 | ## Updating `App` to use the Fetch API
517 |
518 | Let's use what we just learned. We want to make the network request as soon as our component is displayed on screen. The `React.Component` has a lifecycle method, `componentDidMount`, that is called when the component is added to our view. Sounds like this is a good place to make our request:
519 |
520 | ```javascript
521 | componentDidMount() {
522 | const ids = this.state.cities
523 | .map(city => city.id)
524 | .toString();
525 |
526 | fetch(
527 | `http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=${ids}`
528 | )
529 | .then(res => res.json())
530 | .then(body =>
531 | body.list.map(city => {
532 | return {
533 | id: city.id,
534 | name: city.name,
535 | temp: city.main.temp,
536 | icon: 'http://openweathermap.org/img/w/' +
537 | city.weather[0].icon +
538 | '.png',
539 | weather: city.weather[0].main,
540 | };
541 | }))
542 | .then(cities => {
543 | this.setState({
544 | cities: cities,
545 | });
546 | });
547 | }
548 | ```
549 |
550 | Note that as the very last step, we use the `this.setState()` API to update our internal state. By using `setState()`, our component is now aware that its state has been updated. This will in turn trigger a re-render.
551 |
552 | We just need to update the `render()` method now so that it passes all the new data we're getting from our API, to the `CityWeather` component:
553 |
554 | ```javascript
555 | render() {
556 | return (
557 |
558 | {this.state.cities.map(city => (
559 |
566 | ))}
567 |
568 | );
569 | }
570 | ```
571 |
572 | Save your app, and if all goes well and the network works in our favor, you should see a list of cities along with their current weather conditions.
573 |
574 | 
575 |
576 | > Code for Step 8 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step8-Networking.js).
577 |
578 |
579 | ---
580 |
581 | # Step 9: Navigating between screens
582 |
583 | We're almost done with the app. Now there's just one more thing we need to do. Right now our app has just one screen. Most apps have multiple screens, however. We need a way to present multiple screens as well as a way to navigate between them. We can use the `StackNavigator` component from the `react-navigation` library to do so.
584 |
585 | A `StackNavigator` allows us to display a series of screens as a stack. This is a very common pattern in mobile apps: a screen can push a new screen, which can then push another screen onto the stack, and so on. Screens can be popped from the stack at any point, such as when the user taps the "Back" button.
586 |
587 | ## Installing react-navigation and importing StackNavigator
588 |
589 | First, run the following command to install the `react-navigation` library:
590 |
591 | ```bash
592 | npm install --save react-navigation
593 | ```
594 |
595 | Then import the `StackNavigator` component into your project:
596 |
597 | ```javascript
598 | import { StackNavigator } from 'react-navigation';
599 | ```
600 |
601 | ## Using `StackNavigator`
602 |
603 | The StackNavigator API takes a list of screens that will be presented in our app. These screens are mapped to routes. It may look something like this:
604 |
605 | ```
606 | const MainNavigator = StackNavigator({
607 | Master: { screen: MasterScreen },
608 | Detail: { screen: DetailScreen }
609 | });
610 | ```
611 |
612 | The above configuration defines two routes: "Master" and "Detail". These routes will be defined by two screen components, `MasterScreen` and `DetailScreen`, which we haven't created yet.
613 |
614 | ## Creating our screen definitions
615 |
616 | Let's define the screens that our app will display. We already have a component that displays a list of cities in `App`, so we can start by renaming it to `MasterScreen`. We also need to add a title to this screen. Our updated component would look something like this:
617 |
618 | ```javascript
619 | // Rename App to MasterScreen, and configure the title via navigationOptions
620 | export default class MasterScreen extends React.Component {
621 | static navigationOptions = {
622 | title: 'Weather',
623 | };
624 |
625 | // ...
626 | }
627 | ```
628 |
629 | Next, we need to create a `DetailScreen` component to represent our second screen.
630 |
631 | Next, we create our secondary screen, in which we will display additional details about a particular city's weather. Add the following components below the `MasterScreen` definition:
632 |
633 | ```javascript
634 | class DetailScreen extends React.Component {
635 | static navigationOptions = {
636 | title: "Details"
637 | }
638 |
639 | render() {
640 | return (
641 |
642 |
643 | Detailed weather information coming soon.
644 |
645 |
646 | )
647 | }
648 | }
649 | ```
650 |
651 | Now that we have defined our `MasterScreen` and `DetailScreen` components, we can now let `StackNavigator` take care of rendering these components for us by delegating the rendering of our top level component to it:
652 |
653 | ```javascript
654 | class MasterScreen extends React.Component { /* ... */ };
655 | class DetailScreen extends React.Component { /* ... */ };
656 |
657 | export default MainNavigator = StackNavigator({
658 | Master: { screen: MasterScreen },
659 | Detail: { screen: DetailScreen },
660 | })
661 | ```
662 |
663 | Note that we will need to remove "export default" from our `MasterScreen` component.
664 |
665 | Save your app, and notice how your `MasterScreen` component now has a navigation header on top of it. This is provided by the `StackNavigator` and it will display the title for the current screen. It will also display a back button whenever there's more than one screen in the stack.
666 |
667 | ## Navigating between screens
668 |
669 | There's just one more thing to do, and we'll be done. We still need a way to navigate from our main screen to our detail screen. We can tell the navigator to present a new screen by calling the following API:
670 |
671 | ```javascript
672 | navigation.navigate('SCREEN_NAME', { params })
673 | ```
674 |
675 | We want users to be able to tap on a city and have the app display more information about that city's weather. We can achieve this by adding a `onPress` handler to `ListItem` component.
676 |
677 | Go back to `MasterScreen`, and pass an additional `onPress` prop to the `CityWeather` component:
678 |
679 | ```javascript
680 |
687 | () =>
688 | this.props.navigation.navigate('Detail', {
689 | cityId: city.id,
690 | })}
691 | />
692 | ```
693 |
694 | The `navigation` prop is provided by the `StackNavigator` when it renders `MasterScreen`. Now whenever the user taps on a `CityWeather` component, we tell the `StackNavigator` to navigate to the 'Detail' route and display more information about the specific city identified by `cityId`.
695 |
696 | Save your app. Tap on an item in the main screen, and observe how the detail screen animates onto screen.
697 |
698 | 
699 | 
700 |
701 | > Code for Step 9 can be found [here](https://github.com/hramos/ReactNativeClass/blob/master/Steps/App-Step9-Navigation.js).
702 |
703 |
704 | ---
705 |
706 | # Congratulations!
707 |
708 | We have now reached the conclusion of today's class. At this point you should have a basic weather app with the ability to display basic weather data for a given list of cities. The app can also navigate between multiple screens.
709 |
710 | I hope you've learned something new today. Feel free to ask any questions. I will be around the Open Source Dev Garage most of the time and will be happy to help!
711 |
712 | ## Recap
713 |
714 | Let's recap what we've learned today:
715 |
716 | - We used Create React Native App to quickly set up a new project for our app.
717 | - We did not have to recompile the app after each change. Just edit the code, save, and watch the new app get reloaded instantly.
718 | - We created various custom components for use throughout our app.
719 | - Used npm to install additional packages as needed.
720 | - A list of components can be made scrollable using `ScrollView`.
721 | - We used `fetch()` to obtain data from a remote server.
722 | - We used `react-navigation` to display multiple screens in our app.
723 |
724 | ## Feedback
725 |
726 | Please take the survey at https://www.surveymonkey.com/r/f82017oss
727 |
728 | # Homework
729 |
730 | Your homework assignment is to flesh out the `CityDetails` component. Can you make it display additional data such as humidity, wind speed, and minimum/maximum temperatures for the day?
731 |
732 | ## Tips
733 |
734 | - Refer to the official React Native documentation: http://facebook.github.io/react-native/
735 |
736 | - You can access the cityId param passed to our `DetailScreen` component like so:
737 |
738 | ```javascript
739 | const CITY_ID = this.props.navigation.state.params.cityId;
740 | ```
741 |
742 | You can use this city id to query the weather API.
743 |
744 | - The following endpoint will respond with weather data for a single city, given a city id:
745 |
746 | http://api.openweathermap.org/data/2.5/weather?units=imperial&APPID=YOUR_APP_ID&id=CITY_ID
747 |
748 | The response will look something like this:
749 |
750 | ```json
751 | {
752 | coord: {
753 | lon: -121.89,
754 | lat: 37.34,
755 | },
756 | weather: [
757 | {
758 | id: 500,
759 | main: "Rain",
760 | description: "light rain",
761 | icon: "10n",
762 | },
763 | ],
764 | base: "stations",
765 | main: {
766 | temp: 62.28,
767 | pressure: 1021,
768 | humidity: 36,
769 | temp_min: 55.4,
770 | temp_max: 66.2,
771 | },
772 | visibility: 16093,
773 | wind: {
774 | speed: 13.87,
775 | deg: 310,
776 | gust: 10.8,
777 | },
778 | clouds: {
779 | all: 1,
780 | },
781 | dt: 1492216500,
782 | sys: {
783 | type: 1,
784 | id: 482,
785 | message: 0.9189,
786 | country: "US",
787 | sunrise: 1492263143,
788 | sunset: 1492310588,
789 | },
790 | id: 5392171,
791 | name: "San Jose",
792 | cod: 200,
793 | }
794 | ```
795 |
796 | You can see a full sample response by opening http://api.openweathermap.org/data/2.5/weather?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=5392171 in your browser.
797 |
798 | - You can add a `componentDidMount` method to your `DetailScreen` component, where you can make the above network request. Store the additional weather data in your component's state, then adjust the `render` method to display all this new data.
799 |
800 | - Network requests are asynchronous. It's a good idea to show a loading indicator while we wait for the data to come back from the network. You can use the `ActivityIndicator` component for this purpose. Track whether data is available or not in your state.
801 |
802 | - If you get stuck, you can find all of the above implemented in [`App.js`](./WeatherApp/App.js) inside the "WeatherApp" directory in this repo.
803 |
804 | 
805 | 
806 |
--------------------------------------------------------------------------------
/Slides.pdf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Slides.pdf
--------------------------------------------------------------------------------
/Steps/App-CRNA-Starter-Template.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 | Changes you make will automatically reload.
10 | Shake your phone to open the developer menu.
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 |
--------------------------------------------------------------------------------
/Steps/App-CRNA-StarterTemplate.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-CRNA-StarterTemplate.png
--------------------------------------------------------------------------------
/Steps/App-Final-Detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Final-Detail.png
--------------------------------------------------------------------------------
/Steps/App-Final-Master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Final-Master.png
--------------------------------------------------------------------------------
/Steps/App-Step4-LiveReloading.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 | Changes you make will automatically reload.
11 |
12 | Shake your phone to open the developer menu.
13 |
14 | );
15 | }
16 | }
17 |
18 | const styles = StyleSheet.create({
19 | container: {
20 | flex: 1,
21 | backgroundColor: "#fff",
22 | alignItems: "center",
23 | justifyContent: "center",
24 | },
25 | });
26 |
--------------------------------------------------------------------------------
/Steps/App-Step4-LiveReloading.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step4-LiveReloading.png
--------------------------------------------------------------------------------
/Steps/App-Step5-CustomComponents.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 |
4 | class CityWeather extends React.Component {
5 | render() {
6 | return (
7 |
14 |
15 | {this.props.name}
16 |
17 |
18 | {this.props.weather}
19 |
20 |
21 | {this.props.temp} °F
22 |
23 |
24 | );
25 | }
26 | }
27 |
28 | export default class App extends React.Component {
29 | render() {
30 | return (
31 |
32 |
37 |
38 | );
39 | }
40 | }
41 |
42 | const styles = StyleSheet.create({
43 | container: {
44 | flex: 1,
45 | backgroundColor: '#fff',
46 | alignItems: 'center',
47 | justifyContent: 'center',
48 | },
49 | });
50 |
--------------------------------------------------------------------------------
/Steps/App-Step5-CustomComponents.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step5-CustomComponents.png
--------------------------------------------------------------------------------
/Steps/App-Step6-ListItem.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import { StyleSheet, Text, View } from 'react-native';
3 | import { ListItem } from 'react-native-elements';
4 |
5 | class CityWeather extends React.Component {
6 | render() {
7 | return (
8 |
26 | );
27 | }
28 | }
29 |
30 | export default class App extends React.Component {
31 | render() {
32 | return (
33 |
34 |
39 |
40 | );
41 | }
42 | }
43 |
44 | const styles = StyleSheet.create({
45 | container: {
46 | flex: 1,
47 | backgroundColor: '#fff',
48 | alignItems: 'center',
49 | justifyContent: 'center',
50 | },
51 | });
52 |
--------------------------------------------------------------------------------
/Steps/App-Step6-ListItem.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step6-ListItem.png
--------------------------------------------------------------------------------
/Steps/App-Step7-ScrollView.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | Text,
6 | View,
7 | } from 'react-native';
8 | import { ListItem } from 'react-native-elements';
9 |
10 | class CityWeather extends React.Component {
11 | render() {
12 | return (
13 |
31 | );
32 | }
33 | }
34 |
35 | export default class App extends React.Component {
36 | constructor() {
37 | super();
38 |
39 | this.state = {
40 | cities: [
41 | { name: 'San Jose', id: 5392171 },
42 | { name: 'New York', id: 5128581 },
43 | { name: 'London', id: 2643744 },
44 | { name: 'Paris', id: 2968815 },
45 | { name: 'Hong Kong', id: 1819729 },
46 | { name: 'Singapore', id: 1880252 },
47 | { name: 'Beijing', id: 1816670 },
48 | { name: 'Sydney', id: 6619279 },
49 | { name: 'São Paulo', id: 3448439 },
50 | { name: 'San Juan', id: 4568138 },
51 | { name: 'Mumbai', id: 1275339 },
52 | { name: 'Reykjavík', id: 6692263 },
53 | ],
54 | };
55 | }
56 |
57 | render() {
58 | return (
59 |
60 | {this.state.cities.map(city => (
61 |
62 | ))}
63 |
64 | );
65 | }
66 | }
67 |
68 | const styles = StyleSheet.create({
69 | container: {
70 | flex: 1,
71 | backgroundColor: '#fff',
72 | alignItems: 'center',
73 | justifyContent: 'center',
74 | },
75 | });
76 |
--------------------------------------------------------------------------------
/Steps/App-Step7-ScrollView.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step7-ScrollView.png
--------------------------------------------------------------------------------
/Steps/App-Step8-Networking.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | Text,
6 | View,
7 | } from 'react-native';
8 | import { ListItem } from 'react-native-elements';
9 |
10 | class CityWeather extends React.Component {
11 | render() {
12 | return (
13 |
31 | );
32 | }
33 | }
34 |
35 | export default class App extends React.Component {
36 | constructor() {
37 | super();
38 |
39 | this.state = {
40 | cities: [
41 | { name: 'San Jose', id: 5392171 },
42 | { name: 'New York', id: 5128581 },
43 | { name: 'London', id: 2643744 },
44 | { name: 'Paris', id: 2968815 },
45 | { name: 'Hong Kong', id: 1819729 },
46 | { name: 'Singapore', id: 1880252 },
47 | { name: 'Beijing', id: 1816670 },
48 | { name: 'Sydney', id: 6619279 },
49 | { name: 'São Paulo', id: 3448439 },
50 | { name: 'San Juan', id: 4568138 },
51 | { name: 'Mumbai', id: 1275339 },
52 | { name: 'Reykjavík', id: 6692263 },
53 | ],
54 | };
55 | }
56 |
57 | componentDidMount() {
58 | const ids = this.state.cities
59 | .map(city => city.id)
60 | .toString();
61 |
62 | fetch(
63 | `http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=${ids}`
64 | )
65 | .then(res => res.json())
66 | .then(body =>
67 | body.list.map(city => {
68 | return {
69 | id: city.id,
70 | name: city.name,
71 | temp: city.main.temp,
72 | icon: 'http://openweathermap.org/img/w/' +
73 | city.weather[0].icon +
74 | '.png',
75 | weather: city.weather[0].main,
76 | };
77 | }))
78 | .then(cities => {
79 | this.setState({
80 | cities: cities,
81 | });
82 | });
83 | }
84 |
85 | render() {
86 | return (
87 |
88 | {this.state.cities.map(city => (
89 |
96 | ))}
97 |
98 | );
99 | }
100 | }
101 |
102 | const styles = StyleSheet.create({
103 | container: {
104 | flex: 1,
105 | backgroundColor: '#fff',
106 | alignItems: 'center',
107 | justifyContent: 'center',
108 | },
109 | });
110 |
--------------------------------------------------------------------------------
/Steps/App-Step8-Networking.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step8-Networking.png
--------------------------------------------------------------------------------
/Steps/App-Step9-Navigation-Detail.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step9-Navigation-Detail.png
--------------------------------------------------------------------------------
/Steps/App-Step9-Navigation-Master.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/Steps/App-Step9-Navigation-Master.png
--------------------------------------------------------------------------------
/Steps/App-Step9-Navigation.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ScrollView,
4 | StyleSheet,
5 | Text,
6 | View,
7 | } from 'react-native';
8 | import { ListItem } from 'react-native-elements';
9 | import { StackNavigator } from 'react-navigation';
10 |
11 | class CityWeather extends React.Component {
12 | render() {
13 | return (
14 |
32 | );
33 | }
34 | }
35 |
36 | class MasterScreen extends React.Component {
37 | static navigationOptions = {
38 | title: 'Weather',
39 | };
40 |
41 | constructor() {
42 | super();
43 |
44 | this.state = {
45 | cities: [
46 | { name: 'San Jose', id: 5392171 },
47 | { name: 'New York', id: 5128581 },
48 | { name: 'London', id: 2643744 },
49 | { name: 'Paris', id: 2968815 },
50 | { name: 'Hong Kong', id: 1819729 },
51 | { name: 'Singapore', id: 1880252 },
52 | { name: 'Beijing', id: 1816670 },
53 | { name: 'Sydney', id: 6619279 },
54 | { name: 'São Paulo', id: 3448439 },
55 | { name: 'San Juan', id: 4568138 },
56 | { name: 'Mumbai', id: 1275339 },
57 | { name: 'Reykjavík', id: 6692263 },
58 | ],
59 | };
60 | }
61 |
62 | componentDidMount() {
63 | const ids = this.state.cities
64 | .map(city => city.id)
65 | .toString();
66 |
67 | fetch(
68 | `http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=${ids}`
69 | )
70 | .then(res => res.json())
71 | .then(body =>
72 | body.list.map(city => {
73 | return {
74 | id: city.id,
75 | name: city.name,
76 | temp: city.main.temp,
77 | icon: 'http://openweathermap.org/img/w/' +
78 | city.weather[0].icon +
79 | '.png',
80 | weather: city.weather[0].main,
81 | };
82 | }))
83 | .then(cities => {
84 | this.setState({
85 | cities: cities,
86 | });
87 | });
88 | }
89 |
90 | render() {
91 | return (
92 |
93 | {this.state.cities.map(city => (
94 |
101 | () =>
102 | this.props.navigation.navigate('Detail', {
103 | cityId: city.id,
104 | })}
105 | />
106 | ))}
107 |
108 | );
109 | }
110 | }
111 |
112 | class DetailScreen extends React.Component {
113 | static navigationOptions = {
114 | title: 'Details',
115 | };
116 |
117 | render() {
118 | return (
119 |
120 |
121 | Detailed weather information coming soon.
122 |
123 |
124 | );
125 | }
126 | }
127 |
128 | export default (MainNavigator = StackNavigator({
129 | Master: { screen: MasterScreen },
130 | Detail: { screen: DetailScreen },
131 | }));
132 |
133 | const styles = StyleSheet.create({
134 | container: {
135 | flex: 1,
136 | backgroundColor: '#fff',
137 | alignItems: 'center',
138 | justifyContent: 'center',
139 | },
140 | });
141 |
--------------------------------------------------------------------------------
/WeatherApp.gif:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/WeatherApp.gif
--------------------------------------------------------------------------------
/WeatherApp/.DS_Store:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hramos/ReactNativeClass/219d55b9daab88735377b7d78b7c934e274e872a/WeatherApp/.DS_Store
--------------------------------------------------------------------------------
/WeatherApp/.babelrc:
--------------------------------------------------------------------------------
1 | {
2 | "presets": ["babel-preset-expo"],
3 | "env": {
4 | "development": {
5 | "plugins": ["transform-react-jsx-source"]
6 | }
7 | }
8 | }
9 |
--------------------------------------------------------------------------------
/WeatherApp/.flowconfig:
--------------------------------------------------------------------------------
1 | [ignore]
2 | ; We fork some components by platform
3 | .*/*[.]android.js
4 |
5 | ; Ignore "BUCK" generated dirs
6 | /\.buckd/
7 |
8 | ; Ignore unexpected extra "@providesModule"
9 | .*/node_modules/.*/node_modules/fbjs/.*
10 |
11 | ; Ignore duplicate module providers
12 | ; For RN Apps installed via npm, "Libraries" folder is inside
13 | ; "node_modules/react-native" but in the source repo it is in the root
14 | .*/Libraries/react-native/React.js
15 | .*/Libraries/react-native/ReactNative.js
16 |
17 | ; Additional create-react-native-app ignores
18 |
19 | ; Ignore duplicate module providers
20 | .*/node_modules/fbemitter/lib/*
21 |
22 | ; Ignore misbehaving dev-dependencies
23 | .*/node_modules/xdl/build/*
24 | .*/node_modules/reqwest/tests/*
25 |
26 | ; Ignore missing expo-sdk dependencies (temporarily)
27 | ; https://github.com/exponent/exponent-sdk/issues/36
28 | .*/node_modules/expo/src/*
29 |
30 | ; Ignore react-native-fbads dependency of the expo sdk
31 | .*/node_modules/react-native-fbads/*
32 |
33 | [include]
34 |
35 | [libs]
36 | node_modules/react-native/Libraries/react-native/react-native-interface.js
37 | node_modules/react-native/flow
38 | flow/
39 |
40 | [options]
41 | module.system=haste
42 |
43 | emoji=true
44 |
45 | experimental.strict_type_args=true
46 |
47 | munge_underscores=true
48 |
49 | module.name_mapper='^[./a-zA-Z0-9$_-]+\.\(bmp\|gif\|jpg\|jpeg\|png\|psd\|svg\|webp\|m4v\|mov\|mp4\|mpeg\|mpg\|webm\|aac\|aiff\|caf\|m4a\|mp3\|wav\|html\|pdf\)$' -> 'RelativeImageStub'
50 |
51 | suppress_type=$FlowIssue
52 | suppress_type=$FlowFixMe
53 | suppress_type=$FixMe
54 |
55 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixMe\\($\\|[^(]\\|(\\(>=0\\.\\(3[0-8]\\|[1-2][0-9]\\|[0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)
56 | suppress_comment=\\(.\\|\n\\)*\\$FlowIssue\\((\\(>=0\\.\\(3[0-8]\\|1[0-9]\\|[1-2][0-9]\\).[0-9]\\)? *\\(site=[a-z,_]*react_native_oss[a-z,_]*\\)?)\\)?:? #[0-9]+
57 | suppress_comment=\\(.\\|\n\\)*\\$FlowFixedInNextDeploy
58 |
59 | unsafe.enable_getters_and_setters=true
60 |
61 | [version]
62 | ^0.38.0
63 |
--------------------------------------------------------------------------------
/WeatherApp/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | .expo/
3 | npm-debug.*
4 |
--------------------------------------------------------------------------------
/WeatherApp/.watchmanconfig:
--------------------------------------------------------------------------------
1 | {}
2 |
--------------------------------------------------------------------------------
/WeatherApp/App.js:
--------------------------------------------------------------------------------
1 | import React from 'react';
2 | import {
3 | ActivityIndicator,
4 | Platform,
5 | ScrollView,
6 | View,
7 | } from 'react-native';
8 | import { ListItem } from 'react-native-elements';
9 | import { StackNavigator } from 'react-navigation';
10 |
11 | class LoadingIndicator extends React.Component {
12 | render() {
13 | return (
14 |
20 |
21 |
22 | );
23 | }
24 | }
25 |
26 | class CityWeather extends React.Component {
27 | render() {
28 | return (
29 |
44 | );
45 | }
46 | }
47 |
48 | class MasterScreen extends React.Component {
49 | static navigationOptions = {
50 | title: 'Weather',
51 | };
52 |
53 | constructor() {
54 | super();
55 |
56 | this.state = {
57 | cities: [
58 | { name: 'San Jose', id: 5392171 },
59 | { name: 'New York', id: 5128581 },
60 | { name: 'London', id: 2643744 },
61 | { name: 'Paris', id: 2968815 },
62 | { name: 'Hong Kong', id: 1819729 },
63 | { name: 'Singapore', id: 1880252 },
64 | { name: 'Beijing', id: 1816670 },
65 | { name: 'Sydney', id: 6619279 },
66 | { name: 'São Paulo', id: 3448439 },
67 | { name: 'San Juan', id: 4568138 },
68 | { name: 'Mumbai', id: 1275339 },
69 | { name: 'Reykjavík', id: 6692263 },
70 | ],
71 | isLoading: true,
72 | };
73 | }
74 |
75 | componentDidMount() {
76 | const ids = this.state.cities
77 | .map(city => city.id)
78 | .toString();
79 |
80 | fetch(
81 | `http://api.openweathermap.org/data/2.5/group?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=${ids}`
82 | )
83 | .then(res => res.json())
84 | .then(body =>
85 | body.list.map(city => {
86 | return {
87 | id: city.id,
88 | name: city.name,
89 | temp: city.main.temp,
90 | icon: 'http://openweathermap.org/img/w/' +
91 | city.weather[0].icon +
92 | '.png',
93 | weather: city.weather[0].main,
94 | };
95 | }))
96 | .then(cities => {
97 | this.setState({
98 | cities,
99 | isLoading: false,
100 | });
101 | });
102 | }
103 |
104 | render() {
105 | if (this.state.isLoading) {
106 | return ;
107 | }
108 |
109 | return (
110 |
111 | {this.state.cities.map(city => (
112 | {
119 | this.props.navigation.navigate('Detail', {
120 | cityId: city.id,
121 | });
122 | }}
123 | />
124 | ))}
125 |
126 | );
127 | }
128 | }
129 |
130 | class DetailScreen extends React.Component {
131 | static navigationOptions = {
132 | title: 'Details',
133 | };
134 |
135 | constructor() {
136 | super();
137 |
138 | this.state = {
139 | isLoading: true,
140 | };
141 | }
142 |
143 | componentDidMount() {
144 | this.setState({ isLoading: true });
145 |
146 | const cityId = this.props.navigation.state.params.cityId;
147 |
148 | fetch(
149 | `http://api.openweathermap.org/data/2.5/weather?units=imperial&APPID=b1b35bba8b434a28a0be2a3e1071ae5b&id=${cityId}`
150 | )
151 | .then(res => res.json())
152 | .then(jsonRes => {
153 | this.setState({
154 | isLoading: false,
155 | city: {
156 | name: jsonRes.name,
157 | weather: jsonRes.weather[0].main,
158 | temp: jsonRes.main.temp,
159 | temp_min: jsonRes.main.temp_min,
160 | temp_max: jsonRes.main.temp_max,
161 | humidity: jsonRes.main.humidity,
162 | pressure: jsonRes.main.pressure,
163 | wind_speed: jsonRes.wind.speed,
164 | cloudiness: jsonRes.clouds.all,
165 | icon: 'http://openweathermap.org/img/w/' +
166 | jsonRes.weather[0].icon +
167 | '.png',
168 | },
169 | });
170 | });
171 | }
172 |
173 | render() {
174 | if (this.state.isLoading) {
175 | return ;
176 | }
177 |
178 | const { city } = this.state;
179 |
180 | return (
181 |
182 |
194 |
199 |
204 |
209 |
214 |
215 | );
216 | }
217 | }
218 |
219 | export default StackNavigator({
220 | Master: { screen: MasterScreen },
221 | Detail: { screen: DetailScreen },
222 | });
223 |
--------------------------------------------------------------------------------
/WeatherApp/App.test.js:
--------------------------------------------------------------------------------
1 | import React from "react";
2 | import MainNavigator from "./App";
3 |
4 | import renderer from "react-test-renderer";
5 |
6 | it("renders without crashing", () => {
7 | const rendered = renderer.create().toJSON();
8 | expect(rendered).toBeTruthy();
9 | });
10 |
11 | it("renders correctly", () => {
12 | const tree = renderer.create().toJSON();
13 | expect(tree).toMatchSnapshot();
14 | });
15 |
--------------------------------------------------------------------------------
/WeatherApp/README.md:
--------------------------------------------------------------------------------
1 | This project was bootstrapped with [Create React Native App](https://github.com/react-community/create-react-native-app).
2 |
3 | Below you'll find information about performing common tasks. The most recent version of this guide is available [here](https://github.com/react-community/create-react-native-app/blob/master/react-native-scripts/template/README.md).
4 |
5 | ## Table of Contents
6 |
7 | * [Updating to New Releases](#updating-to-new-releases)
8 | * [Available Scripts](#available-scripts)
9 | * [npm start](#npm-start)
10 | * [npm test](#npm-test)
11 | * [npm run ios](#npm-run-ios)
12 | * [npm run android](#npm-run-android)
13 | * [npm run eject](#npm-run-eject)
14 | * [Writing and Running Tests](#writing-and-running-tests)
15 | * [Environment Variables](#environment-variables)
16 | * [Configuring Packager IP Address](#configuring-packager-ip-address)
17 | * [Adding Flow](#adding-flow)
18 | * [Customizing App Display Name and Icon](#customizing-app-display-name-and-icon)
19 | * [Sharing and Deployment](#sharing-and-deployment)
20 | * [Publishing to Expo's React Native Community](#publishing-to-expos-react-native-community)
21 | * [Building an Expo "standalone" app](#building-an-expo-standalone-app)
22 | * [Ejecting from Create React Native App](#ejecting-from-create-react-native-app)
23 | * [Build Dependencies (Xcode & Android Studio)](#build-dependencies-xcode-android-studio)
24 | * [Should I Use ExpoKit?](#should-i-use-expokit)
25 | * [Troubleshooting](#troubleshooting)
26 | * [Networking](#networking)
27 | * [iOS Simulator won't open](#ios-simulator-wont-open)
28 | * [QR Code does not scan](#qr-code-does-not-scan)
29 |
30 | ## Updating to New Releases
31 |
32 | You should only need to update the global installation of `create-react-native-app` very rarely, ideally never.
33 |
34 | Updating the `react-native-scripts` dependency of your app should be as simple as bumping the version number in `package.json` and reinstalling your project's dependencies.
35 |
36 | Upgrading to a new version of React Native requires updating the `react-native`, `react`, and `expo` package versions, and setting the correct `sdkVersion` in `app.json`. See the [versioning guide](https://github.com/react-community/create-react-native-app/blob/master/VERSIONS.md) for up-to-date information about package version compatibility.
37 |
38 | ## Available Scripts
39 |
40 | If Yarn was installed when the project was initialized, then dependencies will have been installed via Yarn, and you should probably use it to run these commands as well. Unlike dependency installation, command running syntax is identical for Yarn and NPM at the time of this writing.
41 |
42 | ### `npm start`
43 |
44 | Runs your app in development mode.
45 |
46 | Open it in the [Expo app](https://expo.io) on your phone to view it. It will reload if you save edits to your files, and you will see build errors and logs in the terminal.
47 |
48 | Sometimes you may need to reset or clear the React Native packager's cache. To do so, you can pass the `--reset-cache` flag to the start script:
49 |
50 | ```
51 | npm start -- --reset-cache
52 | # or
53 | yarn start -- --reset-cache
54 | ```
55 |
56 | #### `npm test`
57 |
58 | Runs the [jest](https://github.com/facebook/jest) test runner on your tests.
59 |
60 | #### `npm run ios`
61 |
62 | Like `npm start`, but also attempts to open your app in the iOS Simulator if you're on a Mac and have it installed.
63 |
64 | #### `npm run android`
65 |
66 | Like `npm start`, but also attempts to open your app on a connected Android device or emulator. Requires an installation of Android build tools (see [React Native docs](https://facebook.github.io/react-native/docs/getting-started.html) for detailed setup). We also recommend installing Genymotion as your Android emulator. Once you've finished setting up the native build environment, there are two options for making the right copy of `adb` available to Create React Native App:
67 |
68 | ##### Using Android Studio's `adb`
69 |
70 | 1. Make sure that you can run adb from your terminal.
71 | 2. Open Genymotion and navigate to `Settings -> ADB`. Select “Use custom Android SDK tools” and update with your [Android SDK directory](https://stackoverflow.com/questions/25176594/android-sdk-location).
72 |
73 | ##### Using Genymotion's `adb`
74 |
75 | 1. Find Genymotion’s copy of adb. On macOS for example, this is normally `/Applications/Genymotion.app/Contents/MacOS/tools/`.
76 | 2. Add the Genymotion tools directory to your path (instructions for [Mac](http://osxdaily.com/2014/08/14/add-new-path-to-path-command-line/), [Linux](http://www.computerhope.com/issues/ch001647.htm), and [Windows](https://www.howtogeek.com/118594/how-to-edit-your-system-path-for-easy-command-line-access/)).
77 | 3. Make sure that you can run adb from your terminal.
78 |
79 | #### `npm run eject`
80 |
81 | This will start the process of "ejecting" from Create React Native App's build scripts. You'll be asked a couple of questions about how you'd like to build your project.
82 |
83 | **Warning:** Running eject is a permanent action (aside from whatever version control system you use). An ejected app will require you to have an [Xcode and/or Android Studio environment](https://facebook.github.io/react-native/docs/getting-started.html) set up.
84 |
85 | ## Customizing App Display Name and Icon
86 |
87 | You can edit `app.json` to include [configuration keys](https://docs.expo.io/versions/latest/guides/configuration.html) under the `expo` key.
88 |
89 | To change your app's display name, set the `expo.name` key in `app.json` to an appropriate string.
90 |
91 | To set an app icon, set the `expo.icon` key in `app.json` to be either a local path or a URL. It's recommended that you use a 512x512 png file with transparency.
92 |
93 | ## Writing and Running Tests
94 |
95 | This project is set up to use [jest](https://facebook.github.io/jest/) for tests. You can configure whatever testing strategy you like, but jest works out of the box. Create test files in directories called `__tests__` to have the files loaded by jest. See the [the template project](https://github.com/react-community/create-react-native-app/tree/master/react-native-scripts/template/__tests__) for an example test. The [jest documentation](https://facebook.github.io/jest/docs/getting-started.html) is also a wonderful resource, as is the [React Native testing tutorial](https://facebook.github.io/jest/docs/tutorial-react-native.html).
96 |
97 | ## Environment Variables
98 |
99 | You can configure some of Create React Native App's behavior using environment variables.
100 |
101 | ### Configuring Packager IP Address
102 |
103 | When starting your project, you'll see something like this for your project URL:
104 |
105 | ```
106 | exp://192.168.0.2:19000
107 | ```
108 |
109 | The "manifest" at that URL tells the Expo app how to retrieve and load your app's JavaScript bundle, so even if you load it in the app via a URL like `exp://localhost:19000`, the Expo client app will still try to retrieve your app at the IP address that the start script provides.
110 |
111 | In some cases, this is less than ideal. This might be the case if you need to run your project inside of a virtual machine and you have to access the packager via a different IP address than the one which prints by default. In order to override the IP address or hostname that is detected by Create React Native App, you can specify your own hostname via the `REACT_NATIVE_PACKAGER_HOSTNAME` environment variable:
112 |
113 | Mac and Linux:
114 |
115 | ```
116 | REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname' npm start
117 | ```
118 |
119 | Windows:
120 | ```
121 | set REACT_NATIVE_PACKAGER_HOSTNAME='my-custom-ip-address-or-hostname'
122 | npm start
123 | ```
124 |
125 | The above example would cause the development server to listen on `exp://my-custom-ip-address-or-hostname:19000`.
126 |
127 | ## Adding Flow
128 |
129 | Flow is a static type checker that helps you write code with fewer bugs. Check out this [introduction to using static types in JavaScript](https://medium.com/@preethikasireddy/why-use-static-types-in-javascript-part-1-8382da1e0adb) if you are new to this concept.
130 |
131 | React Native works with [Flow](http://flowtype.org/) out of the box, as long as your Flow version matches the one used in the version of React Native.
132 |
133 | To add a local dependency to the correct Flow version to a Create React Native App project, follow these steps:
134 |
135 | 1. Find the Flow `[version]` at the bottom of the included [.flowconfig](.flowconfig)
136 | 2. Run `npm install --save-dev flow-bin@x.y.z` (or `yarn add --dev flow-bin@x.y.z`), where `x.y.z` is the .flowconfig version number.
137 | 3. Add `"flow": "flow"` to the `scripts` section of your `package.json`.
138 | 4. Add `// @flow` to any files you want to type check (for example, to `App.js`).
139 |
140 | Now you can run `npm run flow` (or `yarn flow`) to check the files for type errors.
141 | You can optionally use a [plugin for your IDE or editor](https://flow.org/en/docs/editors/) for a better integrated experience.
142 |
143 | To learn more about Flow, check out [its documentation](https://flow.org/).
144 |
145 | ## Sharing and Deployment
146 |
147 | Create React Native App does a lot of work to make app setup and development simple and straightforward, but it's very difficult to do the same for deploying to Apple's App Store or Google's Play Store without relying on a hosted service.
148 |
149 | ### Publishing to Expo's React Native Community
150 |
151 | Expo provides free hosting for the JS-only apps created by CRNA, allowing you to share your app through the Expo client app. This requires registration for an Expo account.
152 |
153 | Install the `exp` command-line tool, and run the publish command:
154 |
155 | ```
156 | $ npm i -g exp
157 | $ exp publish
158 | ```
159 |
160 | ### Building an Expo "standalone" app
161 |
162 | You can also use a service like [Expo's standalone builds](https://docs.expo.io/versions/latest/guides/building-standalone-apps.html) if you want to get an IPA/APK for distribution without having to build the native code yourself.
163 |
164 | ### Ejecting from Create React Native App
165 |
166 | If you want to build and deploy your app yourself, you'll need to eject from CRNA and use Xcode and Android Studio.
167 |
168 | This is usually as simple as running `npm run eject` in your project, which will walk you through the process. Make sure to install `react-native-cli` and follow the [native code getting started guide for React Native](https://facebook.github.io/react-native/docs/getting-started.html).
169 |
170 | #### Should I Use ExpoKit?
171 |
172 | If you have made use of Expo APIs while working on your project, then those API calls will stop working if you eject to a regular React Native project. If you want to continue using those APIs, you can eject to "React Native + ExpoKit" which will still allow you to build your own native code and continue using the Expo APIs. See the [ejecting guide](https://github.com/react-community/create-react-native-app/blob/master/EJECTING.md) for more details about this option.
173 |
174 | ## Troubleshooting
175 |
176 | ### Networking
177 |
178 | If you're unable to load your app on your phone due to a network timeout or a refused connection, a good first step is to verify that your phone and computer are on the same network and that they can reach each other. Create React Native App needs access to ports 19000 and 19001 so ensure that your network and firewall settings allow access from your device to your computer on both of these ports.
179 |
180 | Try opening a web browser on your phone and opening the URL that the packager script prints, replacing `exp://` with `http://`. So, for example, if underneath the QR code in your terminal you see:
181 |
182 | ```
183 | exp://192.168.0.1:19000
184 | ```
185 |
186 | Try opening Safari or Chrome on your phone and loading
187 |
188 | ```
189 | http://192.168.0.1:19000
190 | ```
191 |
192 | and
193 |
194 | ```
195 | http://192.168.0.1:19001
196 | ```
197 |
198 | If this works, but you're still unable to load your app by scanning the QR code, please open an issue on the [Create React Native App repository](https://github.com/react-community/create-react-native-app) with details about these steps and any other error messages you may have received.
199 |
200 | If you're not able to load the `http` URL in your phone's web browser, try using the tethering/mobile hotspot feature on your phone (beware of data usage, though), connecting your computer to that WiFi network, and restarting the packager.
201 |
202 | ### iOS Simulator won't open
203 |
204 | If you're on a Mac, there are a few errors that users sometimes see when attempting to `npm run ios`:
205 |
206 | * "non-zero exit code: 107"
207 | * "You may need to install Xcode" but it is already installed
208 | * and others
209 |
210 | There are a few steps you may want to take to troubleshoot these kinds of errors:
211 |
212 | 1. Make sure Xcode is installed and open it to accept the license agreement if it prompts you. You can install it from the Mac App Store.
213 | 2. Open Xcode's Preferences, the Locations tab, and make sure that the `Command Line Tools` menu option is set to something. Sometimes when the CLI tools are first installed by Homebrew this option is left blank, which can prevent Apple utilities from finding the simulator. Make sure to re-run `npm/yarn run ios` after doing so.
214 | 3. If that doesn't work, open the Simulator, and under the app menu select `Reset Contents and Settings...`. After that has finished, quit the Simulator, and re-run `npm/yarn run ios`.
215 |
216 | ### QR Code does not scan
217 |
218 | If you're not able to scan the QR code, make sure your phone's camera is focusing correctly, and also make sure that the contrast on the two colors in your terminal is high enough. For example, WebStorm's default themes may [not have enough contrast](https://github.com/react-community/create-react-native-app/issues/49) for terminal QR codes to be scannable with the system barcode scanners that the Expo app uses.
219 |
220 | If this causes problems for you, you may want to try changing your terminal's color theme to have more contrast, or running Create React Native App from a different terminal. You can also manually enter the URL printed by the packager script in the Expo app's search bar to load it manually.
221 |
--------------------------------------------------------------------------------
/WeatherApp/__snapshots__/App.test.js.snap:
--------------------------------------------------------------------------------
1 | // Jest Snapshot v1, https://goo.gl/fbAQLP
2 |
3 | exports[`renders correctly 1`] = `
4 |
15 |
23 |
30 |
71 |
80 |
86 |
87 |
88 |
89 |
153 |
160 |
176 |
197 |
219 | Weather
220 |
221 |
222 |
223 |
224 |
225 |
226 |
227 | `;
228 |
--------------------------------------------------------------------------------
/WeatherApp/app.json:
--------------------------------------------------------------------------------
1 | {
2 | "expo": {
3 | "sdkVersion": "15.0.0"
4 | }
5 | }
6 |
--------------------------------------------------------------------------------
/WeatherApp/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "SingleFile",
3 | "version": "0.1.0",
4 | "private": true,
5 | "devDependencies": {
6 | "react-native-scripts": "0.0.27",
7 | "jest-expo": "^0.3.0",
8 | "react-test-renderer": "~15.4.1"
9 | },
10 | "main": "./node_modules/react-native-scripts/build/bin/crna-entry.js",
11 | "scripts": {
12 | "start": "react-native-scripts start",
13 | "eject": "react-native-scripts eject",
14 | "android": "react-native-scripts android",
15 | "ios": "react-native-scripts ios",
16 | "test": "node node_modules/jest/bin/jest.js --watch"
17 | },
18 | "jest": {
19 | "preset": "jest-expo",
20 | "transformIgnorePatterns": [
21 | "node_modules/(?!react-native|react-navigation)/"
22 | ]
23 | },
24 | "dependencies": {
25 | "expo": "^15.1.0",
26 | "react": "~15.4.0",
27 | "react-native": "0.42.3",
28 | "react-native-elements": "^0.10.3",
29 | "react-navigation": "^1.0.0-beta.7"
30 | }
31 | }
32 |
--------------------------------------------------------------------------------