├── .gitignore ├── labs ├── lab-react-bulma-components │ ├── starter-code │ │ └── .gitkeep │ └── README.md ├── lab-react-express-recipes │ ├── starter-code │ │ ├── client │ │ │ └── .gitkeep │ │ └── server │ │ │ └── .gitkeep │ ├── data │ │ ├── ingredients.json │ │ └── dishes.json │ └── README.md ├── lab-react-data-binding │ ├── starter-code │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ └── index.html │ │ ├── src │ │ │ ├── index.js │ │ │ ├── style.css │ │ │ └── foods.json │ │ ├── .gitignore │ │ └── package.json │ ├── README.md │ ├── solution.js │ └── example.html ├── lab-react-ironcontacts │ ├── starter-code │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ └── index.html │ │ ├── src │ │ │ ├── index.css │ │ │ ├── index.js │ │ │ └── contacts.json │ │ ├── .gitignore │ │ └── package.json │ └── README.md ├── lab-react-wiki-countries │ ├── style.css │ ├── README.md │ └── example.html └── lab-axios-functional-programming │ ├── starter-code │ ├── dom-manipulation.js │ ├── index.html │ ├── api.js │ └── expectations.html │ └── README.md ├── labs-solutions ├── lab-react-data-binding │ └── solution-code │ │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ │ ├── .gitignore │ │ ├── package.json │ │ └── src │ │ ├── style.css │ │ ├── foods.json │ │ └── index.js ├── lab-react-ironcontacts │ ├── solution-code │ │ ├── public │ │ │ ├── favicon.ico │ │ │ ├── manifest.json │ │ │ └── index.html │ │ ├── src │ │ │ ├── index.css │ │ │ └── index.js │ │ ├── .gitignore │ │ └── package.json │ └── solution-code-2018-07-04 │ │ ├── public │ │ ├── favicon.ico │ │ ├── manifest.json │ │ └── index.html │ │ ├── src │ │ ├── index.js │ │ ├── style.css │ │ └── foods.json │ │ ├── .gitignore │ │ └── package.json ├── lab-axios-functional-programming │ ├── solution-code │ │ ├── dom-manipulation.js │ │ ├── index.html │ │ ├── api.js │ │ └── expectations.html │ └── README.md └── lab-react-bulma-components │ └── solution-code │ └── src │ └── index.js ├── svg ├── desktop.svg └── github.svg ├── lessons ├── 2-4-react-forms.md ├── 1-3-react-jsx-and-rendering-elements.md ├── 1-2-react-hello-react.md ├── 2-3-react-list-and-keys.md ├── 2-1-react-state-and-lifecycle.md ├── 6-2-react-express-route-guards.md ├── 1-4-react-components-and-props.md ├── extra-react-bootstrap-and-scss.md ├── 2-2-react-handling-events-and-conditional-rendering.md ├── 5-2-react-express-integration.md └── 6-1-react-express-file-upload.md ├── README.md └── summary.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store -------------------------------------------------------------------------------- /labs/lab-react-bulma-components/starter-code/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /labs/lab-react-express-recipes/starter-code/client/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /labs/lab-react-express-recipes/starter-code/server/.gitkeep: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mc100s/module-3-react/HEAD/labs/lab-react-data-binding/starter-code/public/favicon.ico -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mc100s/module-3-react/HEAD/labs/lab-react-ironcontacts/starter-code/public/favicon.ico -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mc100s/module-3-react/HEAD/labs-solutions/lab-react-data-binding/solution-code/public/favicon.ico -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mc100s/module-3-react/HEAD/labs-solutions/lab-react-ironcontacts/solution-code/public/favicon.ico -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mc100s/module-3-react/HEAD/labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/public/favicon.ico -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 14px sans-serif; 3 | max-width: 400px; 4 | margin: auto; 5 | } 6 | 7 | form { 8 | border: 1px solid #ddd; 9 | padding: 10px; 10 | margin: 10px 0; 11 | } 12 | 13 | img { 14 | height: 50px; 15 | } 16 | 17 | table { 18 | margin: auto; 19 | margin-top: 20px; 20 | } -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | font: 14px sans-serif; 3 | max-width: 400px; 4 | margin: auto; 5 | } 6 | 7 | form { 8 | border: 1px solid #ddd; 9 | padding: 10px; 10 | margin: 10px 0; 11 | } 12 | 13 | img { 14 | height: 50px; 15 | } 16 | 17 | table { 18 | margin: auto; 19 | margin-top: 20px; 20 | } -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './style.css'; 4 | 5 | class App extends React.Component { 6 | render() { 7 | return ( 8 |
9 | {/* Your application code */} 10 |
11 | ); 12 | } 13 | } 14 | 15 | ReactDOM.render( 16 | , 17 | document.getElementById('root') 18 | ); -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './style.css'; 4 | 5 | class App extends React.Component { 6 | render() { 7 | return ( 8 |
9 | {/* Your application code */} 10 |
11 | ); 12 | } 13 | } 14 | 15 | ReactDOM.render( 16 | , 17 | document.getElementById('root') 18 | ); -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /svg/desktop.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/ignore-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | 6 | # testing 7 | /coverage 8 | 9 | # production 10 | /build 11 | 12 | # misc 13 | .DS_Store 14 | .env.local 15 | .env.development.local 16 | .env.test.local 17 | .env.production.local 18 | 19 | npm-debug.log* 20 | yarn-debug.log* 21 | yarn-error.log* 22 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "React App", 3 | "name": "Create React App Sample", 4 | "icons": [ 5 | { 6 | "src": "favicon.ico", 7 | "sizes": "64x64 32x32 24x24 16x16", 8 | "type": "image/x-icon" 9 | } 10 | ], 11 | "start_url": "./index.html", 12 | "display": "standalone", 13 | "theme_color": "#000000", 14 | "background_color": "#ffffff" 15 | } 16 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-code", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.1", 7 | "react-dom": "^16.4.1", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-code", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.1", 7 | "react-dom": "^16.4.1", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-code", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.1", 7 | "react-dom": "^16.4.1", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-code", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "react": "^16.4.1", 7 | "react-dom": "^16.4.1", 8 | "react-scripts": "1.1.4" 9 | }, 10 | "scripts": { 11 | "start": "react-scripts start", 12 | "build": "react-scripts build", 13 | "test": "react-scripts test --env=jsdom", 14 | "eject": "react-scripts eject" 15 | } 16 | } -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './index.css'; 5 | 6 | class App extends React.Component { 7 | constructor(props) { 8 | super(props); 9 | } 10 | 11 | render() { 12 | return ( 13 |
14 |

IronContacts

15 |
16 | ); 17 | } 18 | } 19 | 20 | ReactDOM.render( 21 | , 22 | document.getElementById('root') 23 | ); 24 | 25 | 26 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "starter-code", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "bulma": "^0.7.1", 7 | "react": "^16.4.1", 8 | "react-dom": "^16.4.1", 9 | "react-scripts": "1.1.4" 10 | }, 11 | "scripts": { 12 | "start": "react-scripts start", 13 | "build": "react-scripts build", 14 | "test": "react-scripts test --env=jsdom", 15 | "eject": "react-scripts eject" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /labs/lab-react-express-recipes/data/ingredients.json: -------------------------------------------------------------------------------- 1 | [ 2 | { "name": "kosher salt" }, 3 | { "name": "bay leaves" }, 4 | { "name": "brown lentils" }, 5 | { "name": "parsnips" }, 6 | { "name": "sherry vinegar" }, 7 | { "name": "baking potatoes" }, 8 | { "name": "olive oil" }, 9 | { "name": "shallots" }, 10 | { "name": "carrots" }, 11 | { "name": "fat free less sodium chicken broth" }, 12 | { "name": "ground black pepper" }, 13 | { "name": "chopped celery" }, 14 | { "name": "arborio rice" }, 15 | { "name": "olive oil" }, 16 | { "name": "dry white wine" }, 17 | { "name": "brown butter" }, 18 | { "name": "grated parmesan cheese" }, 19 | { "name": "vegetable stock" } 20 | ] 21 | -------------------------------------------------------------------------------- /labs/lab-react-wiki-countries/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 900px; 3 | margin-top: 30px; 4 | } 5 | 6 | .search-bar { 7 | margin-bottom: 30px; 8 | } 9 | 10 | .box { 11 | padding: 0; 12 | max-width: 400px; 13 | } 14 | 15 | .box .media { 16 | align-items: center; 17 | } 18 | 19 | .box img { 20 | height: 100%; 21 | border-top-left-radius: 5px; 22 | border-bottom-left-radius: 5px; 23 | } 24 | 25 | .box input { 26 | width: 100px; 27 | text-align: center; 28 | border: 0px white; 29 | box-shadow: inset 0 1px 2px white 30 | } 31 | 32 | .box .button { 33 | width: 64px; 34 | font-size: 1.3em; 35 | } 36 | 37 | .box input, .box .button { 38 | height: 64px; 39 | } 40 | 41 | ul { 42 | margin-bottom: 10px; 43 | } -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/src/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 900px; 3 | margin-top: 30px; 4 | } 5 | 6 | .search-bar { 7 | margin-bottom: 30px; 8 | } 9 | 10 | .box { 11 | padding: 0; 12 | max-width: 400px; 13 | } 14 | 15 | .box .media { 16 | align-items: center; 17 | } 18 | 19 | .box img { 20 | height: 100%; 21 | border-top-left-radius: 5px; 22 | border-bottom-left-radius: 5px; 23 | } 24 | 25 | .box input { 26 | width: 100px; 27 | text-align: center; 28 | border: 0px white; 29 | box-shadow: inset 0 1px 2px white 30 | } 31 | 32 | .box .button { 33 | width: 64px; 34 | font-size: 1.3em; 35 | } 36 | 37 | .box input, .box .button { 38 | height: 64px; 39 | } 40 | 41 | ul { 42 | margin-bottom: 10px; 43 | } -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/src/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 900px; 3 | margin-top: 30px; 4 | } 5 | 6 | .search-bar { 7 | margin-bottom: 30px; 8 | } 9 | 10 | .box { 11 | padding: 0; 12 | max-width: 400px; 13 | } 14 | 15 | .box .media { 16 | align-items: center; 17 | } 18 | 19 | .box img { 20 | height: 100%; 21 | border-top-left-radius: 5px; 22 | border-bottom-left-radius: 5px; 23 | } 24 | 25 | .box input { 26 | width: 100px; 27 | text-align: center; 28 | border: 0px white; 29 | box-shadow: inset 0 1px 2px white 30 | } 31 | 32 | .box .button { 33 | width: 64px; 34 | font-size: 1.3em; 35 | } 36 | 37 | .box input, .box .button { 38 | height: 64px; 39 | } 40 | 41 | ul { 42 | margin-bottom: 10px; 43 | } -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/src/style.css: -------------------------------------------------------------------------------- 1 | .container { 2 | max-width: 900px; 3 | margin-top: 30px; 4 | } 5 | 6 | .search-bar { 7 | margin-bottom: 30px; 8 | } 9 | 10 | .box { 11 | padding: 0; 12 | max-width: 400px; 13 | } 14 | 15 | .box .media { 16 | align-items: center; 17 | } 18 | 19 | .box img { 20 | height: 100%; 21 | border-top-left-radius: 5px; 22 | border-bottom-left-radius: 5px; 23 | } 24 | 25 | .box input { 26 | width: 100px; 27 | text-align: center; 28 | border: 0px white; 29 | box-shadow: inset 0 1px 2px white 30 | } 31 | 32 | .box .button { 33 | width: 64px; 34 | font-size: 1.3em; 35 | } 36 | 37 | .box input, .box .button { 38 | height: 64px; 39 | } 40 | 41 | ul { 42 | margin-bottom: 10px; 43 | } -------------------------------------------------------------------------------- /labs/lab-axios-functional-programming/starter-code/dom-manipulation.js: -------------------------------------------------------------------------------- 1 | // You can change that value, from 1 to 3 2 | let page = 1 3 | 4 | // You shouldn't modidify the next lines 5 | 6 | displayDataInTheConsole(page) 7 | 8 | 9 | function updateTheDom(elementId) { 10 | return (result) => { 11 | document.getElementById(elementId).innerHTML = JSON.stringify(result, null, 2) 12 | } 13 | } 14 | 15 | getTotalResults(page) 16 | .then(updateTheDom("getTotalResults")) 17 | 18 | getFirstResultName(page) 19 | .then(updateTheDom("getFirstResultName")) 20 | 21 | getNames(page) 22 | .then(updateTheDom("getNames")) 23 | 24 | getIdsAndNames(page) 25 | .then(updateTheDom("getIdsAndNames")) 26 | 27 | getSortedNames(page) 28 | .then(updateTheDom("getSortedNames")) 29 | 30 | getNamesFiltered(page, "m") 31 | .then(updateTheDom("getNamesFiltered")) 32 | 33 | getActorNamesWithTheirKnownForMovies(page) 34 | .then(updateTheDom("getActorNamesWithTheirKnownForMovies")) 35 | -------------------------------------------------------------------------------- /labs-solutions/lab-axios-functional-programming/solution-code/dom-manipulation.js: -------------------------------------------------------------------------------- 1 | // You can change that value, from 1 to 3 2 | let page = 1 3 | 4 | // You shouldn't modidify the next lines 5 | 6 | displayDataInTheConsole(page) 7 | 8 | 9 | function updateTheDom(elementId) { 10 | return (result) => { 11 | document.getElementById(elementId).innerHTML = JSON.stringify(result, null, 2) 12 | } 13 | } 14 | 15 | getTotalResults(page) 16 | .then(updateTheDom("getTotalResults")) 17 | 18 | getFirstResultName(page) 19 | .then(updateTheDom("getFirstResultName")) 20 | 21 | getNames(page) 22 | .then(updateTheDom("getNames")) 23 | 24 | getIdsAndNames(page) 25 | .then(updateTheDom("getIdsAndNames")) 26 | 27 | getSortedNames(page) 28 | .then(updateTheDom("getSortedNames")) 29 | 30 | getNamesFiltered(page, "m") 31 | .then(updateTheDom("getNamesFiltered")) 32 | 33 | getActorNamesWithTheirKnownForMovies(page) 34 | .then(updateTheDom("getActorNamesWithTheirKnownForMovies")) 35 | -------------------------------------------------------------------------------- /lessons/2-4-react-forms.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | Forms 4 | 5 | ## [React | 9. Forms](https://reactjs.org/docs/forms.html) 6 | 7 | **Basic Example with 1 input** 8 | 9 | ```javascript 10 | class NameForm extends React.Component { 11 | constructor(props) { 12 | super(props); 13 | this.state = {value: ''}; 14 | 15 | this.handleChange = this.handleChange.bind(this); 16 | this.handleSubmit = this.handleSubmit.bind(this); 17 | } 18 | 19 | handleChange(event) { 20 | this.setState({value: event.target.value}); 21 | } 22 | 23 | handleSubmit(event) { 24 | alert('A name was submitted: ' + this.state.value); 25 | event.preventDefault(); 26 | } 27 | 28 | render() { 29 | return ( 30 |
31 | 35 | 36 |
37 | ); 38 | } 39 | } 40 | ``` -------------------------------------------------------------------------------- /lessons/1-3-react-jsx-and-rendering-elements.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | JSX and elements 4 | 5 | 6 | ## [React | 1. Hello World](https://reactjs.org/docs/hello-world.html) 7 | 8 | The following code takes the tag with the id `root` and sets its content to `

Hello, world!

` 9 | 10 | ```jsx 11 | ReactDOM.render( 12 |

Hello, world!

, 13 | document.getElementById('root') 14 | ); 15 | ``` 16 | 17 | ## [React | 2. Introducing JSX](https://reactjs.org/docs/introducing-jsx.html) 18 | 19 | You can see JSX as a new type. Now you can store in a variable a JSX as you would store a number for example. 20 | 21 | Be careful in JSX, self closing tag, such as `img`, must be ended by `/>`. 22 | 23 | ```jsx 24 | const element = ; 25 | ``` 26 | 27 | 30 | 31 | 32 | ## [React | 3. Rendering Elements](https://reactjs.org/docs/rendering-elements.html) 33 | 34 | The only thing to remember here is that `ReactDOM.render()` is a function we will use once in the future and that will display our React applciation. 35 | 36 | -------------------------------------------------------------------------------- /labs/lab-axios-functional-programming/starter-code/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Document 8 | 9 | 10 |

Actors manipulations

11 | 12 |

total_results

13 |

14 | 
15 |   

1st result name

16 |

17 | 
18 |   

Names of actors

19 |

20 | 
21 |   

Ids and names of actors

22 |

23 | 
24 |   

Names of actors, sorted by name

25 |

26 | 
27 |   

Names of actors, that has "m" in their name

28 |

29 | 
30 |   

Names of actors, with their "know_for" movies

31 |

32 |   
33 |   
34 |   
35 |   
36 | 
37 | 


--------------------------------------------------------------------------------
/labs-solutions/lab-axios-functional-programming/solution-code/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 | 
 4 |   
 5 |   
 6 |   
 7 |   Document
 8 | 
 9 | 
10 |   

Actors manipulations

11 | 12 |

total_results

13 |

14 | 
15 |   

1st result name

16 |

17 | 
18 |   

Names of actors

19 |

20 | 
21 |   

Ids and names of actors

22 |

23 | 
24 |   

Names of actors, sorted by name

25 |

26 | 
27 |   

Names of actors, that has "m" in their name

28 |

29 | 
30 |   

Names of actors, with their "know_for" movies

31 |

32 |   
33 |   
34 |   
35 |   
36 | 
37 | 


--------------------------------------------------------------------------------
/svg/github.svg:
--------------------------------------------------------------------------------
1 | 


--------------------------------------------------------------------------------
/labs-solutions/lab-react-bulma-components/solution-code/src/index.js:
--------------------------------------------------------------------------------
 1 | 
 2 | // The Button Component for iteration 1
 3 | class Button extends React.Component {
 4 |   constructor(props) {
 5 |     super(props);
 6 |   }
 7 |   
 8 |   getClassName() {
 9 |     let res = 'button';
10 |     if (this.props.className)
11 |     res += ' ' + this.props.className;
12 |     let correspondances = {
13 |       isActive: 'is-active',
14 |       isBlack: 'is-black',
15 |       isCentered: 'is-centered',
16 |       isDanger: 'is-danger',
17 |       isDark: 'is-dark',
18 |       isFocused: 'is-focused',
19 |       isGrouped: 'is-grouped',
20 |       isHovered: 'is-hovered',
21 |       isInfo: 'is-info',
22 |       isInverted: 'is-inverted',
23 |       isLarge: 'is-large',
24 |       isLight: 'is-light',
25 |       isLink: 'is-link',
26 |       isLoading: 'is-loading',
27 |       isMedium: 'is-medium',
28 |       isOutlined: 'is-outlined',
29 |       isPrimary: 'is-primary',
30 |       isRight: 'is-right',
31 |       isRounded: 'is-rounded',
32 |       isSelected: 'is-selected',
33 |       isSmall: 'is-small',
34 |       isStatic: 'is-static',
35 |       isSuccess: 'is-success',
36 |       isText: 'is-text',
37 |       isWarning: 'is-warning',
38 |       isWhite: 'is-white',
39 |     };
40 |     for (const key in correspondances) {
41 |       if (this.props[key]) {
42 |         res += ' ' + correspondances[key];
43 |       }
44 |     }
45 |     return res;
46 |   }
47 |   
48 |   render() {
49 |     return (
50 |       
51 |     )
52 |   }
53 | }


--------------------------------------------------------------------------------
/lessons/1-2-react-hello-react.md:
--------------------------------------------------------------------------------
 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png)
 2 | 
 3 | # React | Hello React
 4 | 
 5 | 
 6 | The main new topic of that module is React, a front-end framework.
 7 | 
 8 | ## The main front-end frameworks
 9 | 
10 | ![](https://cdn-images-1.medium.com/max/1600/1*aPijhbTjT0VOxPYq2RkVUw.png)
11 | ![](https://i.imgur.com/idjem4b.png)
12 | 
13 | 
14 | http://www.npmtrends.com/react-vs-@angular/core-vs-vue
15 | 
16 | ## MERN Stack
17 | 
18 | We will include React in another architecture called the MERN Stack.
19 | 
20 | 
21 | ![](https://i.imgur.com/Ef9eH8s.png)
22 | ![](https://webassets.mongodb.com/_com_assets/cms/MERN_stack-y11tmdeja3.png)
23 | 
24 | 
25 | ## The documentation we will use
26 | 
27 | - [The official React documentation](https://reactjs.org/docs/)
28 | - [The React Router documentation](https://reacttraining.com/react-router/web/guides/)
29 | 
30 | 
31 | ## [React | Installation](https://reactjs.org/docs/try-react.html)
32 | 
33 | ### With Codepen 
34 | To use React, you can either play on Codepen with this [Hello World React example on Codepen](https://codepen.io/pen?&editors=0010).
35 | 
36 | ### With CodeSanbox
37 | 
38 | If you want something very quick to use, very close to what you can have with VS Code, you can go on CodeSanbox.io: https://codesandbox.io/s/new
39 | 
40 | ### With `create-react-app` 
41 | Or you can create a React application with your terminal:
42 | 
43 | ```
44 | $ npm install -g create-react-app
45 | $ create-react-app my-app
46 | $ cd my-app
47 | $ npm start
48 | ```
49 | 
50 | 
51 | ## [React | Tutorial](https://reactjs.org/tutorial/tutorial.html)
52 | 
53 | You can follow the tutorial, it will give you an overview.
54 | 
55 | It is doable if you don't have any React knowledge, but it is going to be hard.
56 | 


--------------------------------------------------------------------------------
/labs/lab-react-data-binding/starter-code/public/index.html:
--------------------------------------------------------------------------------
 1 | 
 2 | 
 3 |   
 4 |     
 5 |     
 6 |     
 7 |     
11 |     
12 |     
13 |     
22 |     React App
23 |   
24 |   
25 |     
28 |     
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /lessons/2-3-react-list-and-keys.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | Lists and Keys 4 | 5 | 6 | ## [React | 8. List and Keys](https://reactjs.org/docs/lists-and-keys.html) 7 | 8 | 9 | ```javascript 10 | const students = ['Alice', 'Bob', 'Charly', 'David'] 11 | 12 | class MyComponent extends React.Component { 13 | showList() { 14 | let list = [] 15 | for (let i = 0; i < students.length; i++) { 16 | list.push(
  • {students[i]}
  • ) 17 | } 18 | return list 19 | } 20 | render() { 21 | let list = [] 22 | for (let i = 0; i < students.length; i++) { 23 | list.push(
  • {students[i]}
  • ) 24 | } 25 | return ( 26 |
      27 | {/********** Method 1: Variable **********/} 28 | {list} 29 | {/********** Method 2: Function **********/} 30 | {this.showList()} 31 | {/********** Method 3: Map **********/} 32 | {students.map(student =>
    • {student}
    • )} 33 |
    34 | ) 35 | } 36 | } 37 | ``` 38 | 39 | 40 | ### Exercise 41 | 42 | Redo the exercise where you need to display detail about cities, by using a loop this time. 43 | 44 | The pen is available [here](https://codepen.io/maxencebouret/pen/zaayEZ?editors=0010) and you can use the following `CityDetail` Component. 45 | 46 | ```jsx 47 | class CityDetail extends React.Component { 48 | render(){ 49 | return ( 50 |
    51 | 52 |

    {this.props.name}

    53 |

    {this.props.description}

    54 |
    55 | ) 56 | } 57 | } 58 | ``` 59 | 60 | 61 | You will find a solution here, but look at it only when you are done: https://codepen.io/maxencebouret/pen/YvvdjO?editors=0010 62 | 63 | 64 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 11 | 12 | 13 | 22 | React App 23 | 24 | 25 | 28 |
    29 | 39 | 40 | 41 | -------------------------------------------------------------------------------- /labs/lab-axios-functional-programming/starter-code/api.js: -------------------------------------------------------------------------------- 1 | let service = axios.create({ 2 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 3 | }) 4 | 5 | function displayDataInTheConsole(page) { 6 | return service.get(`result-${page}.json`) 7 | .then(response => { 8 | console.log('response.data ==> ', response.data); 9 | }) 10 | } 11 | 12 | function getTotalResults(page) { 13 | return service.get(`page-${page}.json`) 14 | .then(response => { 15 | // TODO: Iteration 1 16 | // Update that function so it only displays the value of "total_results" (18966) 17 | return response.data // You should write it "response.data.something" 18 | }) 19 | } 20 | 21 | function getFirstResultName(page) { 22 | return service.get(`page-${page}.json`) 23 | .then(response => { 24 | // TODO: Iteration 2 25 | // Update that function so it only displays the name of the first actor 26 | return response.data 27 | }) 28 | } 29 | 30 | function getNames(page) { 31 | return service.get(`page-${page}.json`) 32 | .then(response => { 33 | // TODO: Iteration 3 34 | }) 35 | } 36 | 37 | function getIdsAndNames(page) { 38 | return service.get(`page-${page}.json`) 39 | .then(response => { 40 | // TODO: Iteration 4 41 | }) 42 | } 43 | 44 | function getSortedNames(page) { 45 | return service.get(`page-${page}.json`) 46 | .then(response => { 47 | // TODO: Iteration 5 48 | }) 49 | } 50 | 51 | function getNamesFiltered(page, searchTerm) { 52 | return service.get(`page-${page}.json`) 53 | .then(response => { 54 | // TODO: Iteration 6 55 | }) 56 | } 57 | 58 | 59 | function getActorNamesWithTheirKnownForMovies(page) { 60 | return service.get(`page-${page}.json`) 61 | .then(response => { 62 | // TODO: Iteration 7 63 | }) 64 | } -------------------------------------------------------------------------------- /lessons/2-1-react-state-and-lifecycle.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | State and Lifecycle 4 | 5 | 6 | ## [React | 5. State and Lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) 7 | 8 | ### About the state 9 | 10 | ```jsx 11 | // You can initialize the state in the Component's constructor 12 | this.state = { firstname: 'Maxence', age: 25 } 13 | 14 | // You can get a state value with "this.state" property 15 | this.state.firstname 16 | 17 | // You MUST set some state value with "this.setState" method 18 | // Be careful, this opereation might be asynchronous 19 | this.setState({firstname: 'Mickaël'}) 20 | ``` 21 | 22 | ### About React Lifecycle 23 | 24 | Mounting: 25 | - [`constructor(props)`](https://reactjs.org/docs/react-component.html#constructor): Should starts with `super(props)`; Perfect to initialize the state and binding methods 26 | - [`render()`](https://reactjs.org/docs/react-component.html#render): Return the JSX to display 27 | - [`componentDidMount()`](https://reactjs.org/docs/react-component.html#componentdidmount): Perfect place to call APIs and set up any subscriptions. 28 | 29 | Updating: 30 | - [`componentWillUpdate(nextProps, nextState)`](https://reactjs.org/docs/react-component.html#componentwillupdate) 31 | - [`render()`](https://reactjs.org/docs/react-component.html#render) 32 | - [`componentDidUpdate(prevProps, prevState)`](https://reactjs.org/docs/react-component.html#componentdidupdate) 33 | 34 | Unmounting: 35 | - [`componentWillUnmount()`](https://reactjs.org/docs/react-component.html#componentwillunmount) 36 | 37 | Error Handling: 38 | - [`componentDidCatch()`](https://reactjs.org/docs/react-component.html#componentdidcatch) 39 | 40 | ### Exercise 41 | 42 | [On this Codepen](https://codepen.io/maxencebouret/pen/bKKzwp?editors=0010), by using `axios`, display in the `App` component the information from this API: `https://ih-crud-api.herokuapp.com/characters/1` 43 | 44 | -------------------------------------------------------------------------------- /labs-solutions/lab-axios-functional-programming/solution-code/api.js: -------------------------------------------------------------------------------- 1 | let service = axios.create({ 2 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 3 | }) 4 | 5 | function displayDataInTheConsole(page) { 6 | return service.get(`page-${page}.json`) 7 | .then(response => { 8 | console.log('response.data ==> ', response.data); 9 | }) 10 | } 11 | 12 | function getTotalResults(page) { 13 | return service.get(`page-${page}.json`) 14 | .then(response => { 15 | return response.data.total_results 16 | }) 17 | } 18 | 19 | function getFirstResultName(page) { 20 | return service.get(`page-${page}.json`) 21 | .then(response => { 22 | return response.data.results[0].name 23 | }) 24 | } 25 | 26 | function getNames(page) { 27 | return service.get(`page-${page}.json`) 28 | .then(response => { 29 | return response.data.results.map(x => x.name) 30 | }) 31 | } 32 | 33 | function getIdsAndNames(page) { 34 | return service.get(`page-${page}.json`) 35 | .then(response => { 36 | return response.data.results.map(x => `#${x.id} ${x.name}`) 37 | }) 38 | } 39 | 40 | function getSortedNames(page) { 41 | return service.get(`page-${page}.json`) 42 | .then(response => { 43 | return response.data.results 44 | .map(x => x.name) 45 | .sort() 46 | }) 47 | } 48 | 49 | function getNamesFiltered(page, searchTerm) { 50 | return service.get(`page-${page}.json`) 51 | .then(response => { 52 | return response.data.results 53 | .map(x => x.name) 54 | .filter(name => name.toUpperCase().includes(searchTerm.toUpperCase())) 55 | }) 56 | } 57 | 58 | 59 | function getActorNamesWithTheirKnownForMovies(page) { 60 | return service.get(`page-${page}.json`) 61 | .then(response => { 62 | return response.data.results 63 | .map(x => { 64 | let titles = x.known_for.map(movie => movie.title) 65 | return x.name + " ("+titles.join(", ")+")" 66 | }) 67 | }) 68 | } -------------------------------------------------------------------------------- /lessons/6-2-react-express-route-guards.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React & Express | Route guards 4 | 5 | ## Introduction 6 | 7 | In this course, we are going to update the project IronTodos. We will add a role to users, `ADMIN` and `USER`, and only the `ADMIN` will be able to delete the todo item. 8 | 9 | ## Set up the project 10 | 11 | ```sh 12 | $ git clone https://github.com/mc100s/react-iron-todos.git 13 | $ cd react-iron-todos 14 | $ npm install 15 | 16 | # In 2 different terminals 17 | $ npm run dev:server 18 | $ npm run dev:client 19 | ``` 20 | 21 | 22 | 23 | ## Files to modify 24 | 25 | 26 | ```js 27 | // server/models/user.js 28 | role: {type: String, enum: ['ADMIN', 'USER'], default: 'USER'} 29 | ``` 30 | 31 | 32 | ```js 33 | // server/routes/todos.js 34 | 35 | // OPTION 1 36 | function checkRole(role) { 37 | return [ 38 | passport.authenticate("jwt", config.jwtSession), 39 | (req,res,next) => { 40 | console.log('DEBUG req.user', req.user); 41 | if (req.user.role === role) 42 | next(); 43 | else 44 | next("You don't have the rights") 45 | } 46 | ] 47 | } 48 | router.delete('/:id', checkRole('ADMIN'), (req, res, next) => { 49 | console.log('DEBUG req.user', req.user); 50 | Todo.findByIdAndRemove(req.params.id) 51 | .then(todo=>{ 52 | res.json({success: true, todo}) 53 | }) 54 | .catch(error=>next(error)) 55 | }); 56 | 57 | // OR 58 | 59 | // OPTION 2 60 | router.delete('/:id', passport.authenticate("jwt", config.jwtSession), (req, res, next) => { 61 | if (req,user.role !== 'ADMIN') { 62 | res.json({success: false, message: "You don't have the rights"}) 63 | return; 64 | } 65 | Todo.findByIdAndRemove(req.params.id) 66 | .then(todo=>{ 67 | res.json({success: true, todo}) 68 | }) 69 | .catch(error=>next(error)) 70 | }); 71 | ``` 72 | 73 | ```js 74 | // client/src/api.js 75 | 76 | loadRole() { 77 | let user = this.loadUser() 78 | if (!user || !user.role) return false 79 | return user.role 80 | }, 81 | + 82 | 83 | ``` 84 | 85 | ```js 86 | // client/src/components/Home.js 87 | 88 | import api from '../api'; 89 | 90 | // ... 91 | {api.loadRole() === "ADMIN" && } 92 | // ... 93 | ``` 94 | 95 | 96 | 97 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/starter-code/src/foods.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pizza", 4 | "calories": 400, 5 | "image": "https://i.imgur.com/eTmWoAN.png", 6 | "quantity": 0 7 | }, 8 | { 9 | "name": "Salad", 10 | "calories": 150, 11 | "image": "https://i.imgur.com/DupGBz5.jpg", 12 | "quantity": 0 13 | }, 14 | { 15 | "name": "Sweet Potato", 16 | "calories": 120, 17 | "image": "https://i.imgur.com/hGraGyR.jpg", 18 | "quantity": 0 19 | }, 20 | { 21 | "name": "Gnocchi", 22 | "calories": 500, 23 | "image": "https://i.imgur.com/93ekwW0.jpg", 24 | "quantity": 0 25 | }, 26 | { 27 | "name": "Pot Roast", 28 | "calories": 350, 29 | "image": "https://i.imgur.com/WCzJDWz.jpg", 30 | "quantity": 0 31 | }, 32 | { 33 | "name": "Lasagna", 34 | "calories": 750, 35 | "image": "https://i.imgur.com/ClxOafl.jpg", 36 | "quantity": 0 37 | }, 38 | { 39 | "name": "Hamburger", 40 | "calories": 400, 41 | "image": "https://i.imgur.com/LoG39wK.jpg", 42 | "quantity": 0 43 | }, 44 | { 45 | "name": "Pad Thai", 46 | "calories": 475, 47 | "image": "https://i.imgur.com/5ktcSzF.jpg", 48 | "quantity": 0 49 | }, 50 | { 51 | "name": "Almonds", 52 | "calories": 75, 53 | "image": "https://i.imgur.com/JRp4Ksx.jpg", 54 | "quantity": 0 55 | }, 56 | { 57 | "name": "Bacon", 58 | "calories": 175, 59 | "image": "https://i.imgur.com/7GlqDsG.jpg", 60 | "quantity": 0 61 | }, 62 | { 63 | "name": "Hot Dog", 64 | "calories": 275, 65 | "image": "https://i.imgur.com/QqVHdRu.jpg", 66 | "quantity": 0 67 | }, 68 | { 69 | "name": "Chocolate Cake", 70 | "calories": 490, 71 | "image": "https://i.imgur.com/yrgzA9x.jpg", 72 | "quantity": 0 73 | }, 74 | { 75 | "name": "Wheat Bread", 76 | "calories": 175, 77 | "image": "https://i.imgur.com/TsWzMfM.jpg", 78 | "quantity": 0 79 | }, 80 | { 81 | "name": "Orange", 82 | "calories": 85, 83 | "image": "https://i.imgur.com/abKGOcv.jpg", 84 | "quantity": 0 85 | }, 86 | { 87 | "name": "Banana", 88 | "calories": 175, 89 | "image": "https://i.imgur.com/BMdJhu5.jpg", 90 | "quantity": 0 91 | }, 92 | { 93 | "name": "Yogurt", 94 | "calories": 125, 95 | "image": "https://i.imgur.com/URhdrAm.png", 96 | "quantity": 0 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/src/foods.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pizza", 4 | "calories": 400, 5 | "image": "https://i.imgur.com/eTmWoAN.png", 6 | "quantity": 0 7 | }, 8 | { 9 | "name": "Salad", 10 | "calories": 150, 11 | "image": "https://i.imgur.com/DupGBz5.jpg", 12 | "quantity": 0 13 | }, 14 | { 15 | "name": "Sweet Potato", 16 | "calories": 120, 17 | "image": "https://i.imgur.com/hGraGyR.jpg", 18 | "quantity": 0 19 | }, 20 | { 21 | "name": "Gnocchi", 22 | "calories": 500, 23 | "image": "https://i.imgur.com/93ekwW0.jpg", 24 | "quantity": 0 25 | }, 26 | { 27 | "name": "Pot Roast", 28 | "calories": 350, 29 | "image": "https://i.imgur.com/WCzJDWz.jpg", 30 | "quantity": 0 31 | }, 32 | { 33 | "name": "Lasagna", 34 | "calories": 750, 35 | "image": "https://i.imgur.com/ClxOafl.jpg", 36 | "quantity": 0 37 | }, 38 | { 39 | "name": "Hamburger", 40 | "calories": 400, 41 | "image": "https://i.imgur.com/LoG39wK.jpg", 42 | "quantity": 0 43 | }, 44 | { 45 | "name": "Pad Thai", 46 | "calories": 475, 47 | "image": "https://i.imgur.com/5ktcSzF.jpg", 48 | "quantity": 0 49 | }, 50 | { 51 | "name": "Almonds", 52 | "calories": 75, 53 | "image": "https://i.imgur.com/JRp4Ksx.jpg", 54 | "quantity": 0 55 | }, 56 | { 57 | "name": "Bacon", 58 | "calories": 175, 59 | "image": "https://i.imgur.com/7GlqDsG.jpg", 60 | "quantity": 0 61 | }, 62 | { 63 | "name": "Hot Dog", 64 | "calories": 275, 65 | "image": "https://i.imgur.com/QqVHdRu.jpg", 66 | "quantity": 0 67 | }, 68 | { 69 | "name": "Chocolate Cake", 70 | "calories": 490, 71 | "image": "https://i.imgur.com/yrgzA9x.jpg", 72 | "quantity": 0 73 | }, 74 | { 75 | "name": "Wheat Bread", 76 | "calories": 175, 77 | "image": "https://i.imgur.com/TsWzMfM.jpg", 78 | "quantity": 0 79 | }, 80 | { 81 | "name": "Orange", 82 | "calories": 85, 83 | "image": "https://i.imgur.com/abKGOcv.jpg", 84 | "quantity": 0 85 | }, 86 | { 87 | "name": "Banana", 88 | "calories": 175, 89 | "image": "https://i.imgur.com/BMdJhu5.jpg", 90 | "quantity": 0 91 | }, 92 | { 93 | "name": "Yogurt", 94 | "calories": 125, 95 | "image": "https://i.imgur.com/URhdrAm.png", 96 | "quantity": 0 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code-2018-07-04/src/foods.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pizza", 4 | "calories": 400, 5 | "image": "https://i.imgur.com/eTmWoAN.png", 6 | "quantity": 0 7 | }, 8 | { 9 | "name": "Salad", 10 | "calories": 150, 11 | "image": "https://i.imgur.com/DupGBz5.jpg", 12 | "quantity": 0 13 | }, 14 | { 15 | "name": "Sweet Potato", 16 | "calories": 120, 17 | "image": "https://i.imgur.com/hGraGyR.jpg", 18 | "quantity": 0 19 | }, 20 | { 21 | "name": "Gnocchi", 22 | "calories": 500, 23 | "image": "https://i.imgur.com/93ekwW0.jpg", 24 | "quantity": 0 25 | }, 26 | { 27 | "name": "Pot Roast", 28 | "calories": 350, 29 | "image": "https://i.imgur.com/WCzJDWz.jpg", 30 | "quantity": 0 31 | }, 32 | { 33 | "name": "Lasagna", 34 | "calories": 750, 35 | "image": "https://i.imgur.com/ClxOafl.jpg", 36 | "quantity": 0 37 | }, 38 | { 39 | "name": "Hamburger", 40 | "calories": 400, 41 | "image": "https://i.imgur.com/LoG39wK.jpg", 42 | "quantity": 0 43 | }, 44 | { 45 | "name": "Pad Thai", 46 | "calories": 475, 47 | "image": "https://i.imgur.com/5ktcSzF.jpg", 48 | "quantity": 0 49 | }, 50 | { 51 | "name": "Almonds", 52 | "calories": 75, 53 | "image": "https://i.imgur.com/JRp4Ksx.jpg", 54 | "quantity": 0 55 | }, 56 | { 57 | "name": "Bacon", 58 | "calories": 175, 59 | "image": "https://i.imgur.com/7GlqDsG.jpg", 60 | "quantity": 0 61 | }, 62 | { 63 | "name": "Hot Dog", 64 | "calories": 275, 65 | "image": "https://i.imgur.com/QqVHdRu.jpg", 66 | "quantity": 0 67 | }, 68 | { 69 | "name": "Chocolate Cake", 70 | "calories": 490, 71 | "image": "https://i.imgur.com/yrgzA9x.jpg", 72 | "quantity": 0 73 | }, 74 | { 75 | "name": "Wheat Bread", 76 | "calories": 175, 77 | "image": "https://i.imgur.com/TsWzMfM.jpg", 78 | "quantity": 0 79 | }, 80 | { 81 | "name": "Orange", 82 | "calories": 85, 83 | "image": "https://i.imgur.com/abKGOcv.jpg", 84 | "quantity": 0 85 | }, 86 | { 87 | "name": "Banana", 88 | "calories": 175, 89 | "image": "https://i.imgur.com/BMdJhu5.jpg", 90 | "quantity": 0 91 | }, 92 | { 93 | "name": "Yogurt", 94 | "calories": 125, 95 | "image": "https://i.imgur.com/URhdrAm.png", 96 | "quantity": 0 97 | } 98 | ] 99 | -------------------------------------------------------------------------------- /lessons/1-4-react-components-and-props.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | Components and Props 4 | 5 | 6 | ## Atomic Design 7 | 8 | Before starting, let's spend time on Atomic Design, a pattern to design websites with components. 9 | 10 | ![](https://i.imgur.com/PIVSiPy.png) 11 | 12 | If you are interested, you can have a look on [that blog post from Brad Frost](http://bradfrost.com/blog/post/atomic-web-design/). 13 | 14 | ## [React | 4. Components and Props](https://reactjs.org/docs/components-and-props.html) 15 | 16 | From now on, we will create React Component like this: 17 | 18 | ```jsx 19 | class Welcome extends React.Component { 20 | render() { 21 | return

    Hello, {this.props.name}

    ; 22 | 23 | } 24 | } 25 | ``` 26 | 27 | When a component only have a `render` method, you can write it like this: 28 | 29 | ```jsx 30 | function Welcome(props) { 31 | return

    Hello, {props.name}

    ; 32 | } 33 | ``` 34 | 35 | 36 | ```jsx 37 | class Welcome extends React.Component { 38 | render() { 39 | return

    Hello, {this.props.name}

    ; 40 | } 41 | } 42 | 43 | class App extends React.Component { 44 | render(){ 45 | return ( 46 |
    47 | 48 | 49 | 50 |
    51 | ); 52 | } 53 | } 54 | 55 | ReactDOM.render( 56 | , 57 | document.getElementById('root') 58 | ); 59 | ``` 60 | 61 | 62 | ### Exercise 63 | 64 | Create a React Component `CityDetail` that will render a `div` similar to this: 65 | 66 | ```html 67 |
    68 | 69 |

    Berlin

    70 |

    Berlin is the capital and the largest city of Germany, as well as one of its 16 constituent states

    71 |
    72 | ``` 73 | 74 | Be careful in that example, in JSX you will need to close the `img` tag and change `class` into `className` 75 | 76 | 77 | Then you will need to reuse this Component three times to display information from this array: 78 | 79 | ```js 80 | let cities = [{ 81 | name: "Berlin", 82 | description: "Berlin is the capital and the largest city of Germany, as well as one of its 16 constituent states", 83 | imgUrl: "https://i.imgur.com/v7vGkhm.jpg" 84 | },{ 85 | name: "Paris", 86 | description: "Paris is the capital and most populous city of France, with an area of 105 square kilometres (41 square miles) and a population of 2,206,488", 87 | imgUrl: "https://i.imgur.com/TVZKjza.jpg" 88 | },{ 89 | name: "Madrid", 90 | description: "Madrid is the capital of Spain and the largest municipality in both the Community of Madrid and Spain as a whole", 91 | imgUrl: "https://i.imgur.com/4Eno2jp.jpg" 92 | }] 93 | ``` 94 | 95 | 96 | You can rely on this Pen for this exercise: https://codepen.io/maxencebouret/pen/zaayEZ?editors=0010 97 | 98 | -------------------------------------------------------------------------------- /labs-solutions/lab-react-ironcontacts/solution-code/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './index.css'; 5 | import contacts from './contacts.json' 6 | 7 | class AddRandomContactButton extends React.Component { 8 | render() { 9 | return ( 10 | 11 | ); 12 | } 13 | } 14 | 15 | class SortButton extends React.Component { 16 | render() { 17 | return ( 18 | 19 | ); 20 | } 21 | } 22 | 23 | class ContactRow extends React.Component { 24 | render() { 25 | return ( 26 | 27 | Picture 28 | {this.props.contact.name} 29 | {this.props.contact.popularity.toFixed(2)} 30 | 31 | 32 | ); 33 | } 34 | } 35 | 36 | class App extends React.Component { 37 | constructor(props) { 38 | super(props); 39 | this.state = { 40 | contacts: contacts.slice(0,5) 41 | } 42 | } 43 | 44 | addRandomContact() { 45 | let newContacts = this.state.contacts.slice(); 46 | newContacts.push(contacts[Math.floor(Math.random()*contacts.length)]) 47 | this.setState({ 48 | contacts: newContacts 49 | }) 50 | } 51 | 52 | removeContact(indexContact) { 53 | let newContacts = this.state.contacts.slice(); 54 | newContacts.splice(indexContact, 1); 55 | this.setState({ 56 | contacts: newContacts 57 | }) 58 | } 59 | 60 | sortContacts(field) { 61 | let newContacts = this.state.contacts.slice(); 62 | newContacts.sort((a,b) => { 63 | return a[field] > b[field] ? 1 : -1; 64 | }); 65 | this.setState({ 66 | contacts: newContacts 67 | }) 68 | } 69 | 70 | displayContacts() { 71 | let result = []; 72 | for (let i = 0; i < this.state.contacts.length; i++) { 73 | result.push( 74 | {this.removeContact(i)}} 78 | /> 79 | ) 80 | } 81 | return result; 82 | } 83 | 84 | render() { 85 | return ( 86 |
    87 |

    IronContacts

    88 | {this.addRandomContact()}} /> 89 | {this.sortContacts(field)}} /> 90 | {this.sortContacts(field)}} /> 91 | 92 | 93 | 94 | 95 | 96 | 97 | 98 | 99 | 100 | 101 | {this.displayContacts()} 102 | 103 |
    PictureNamePopularityAction
    104 |
    105 | ); 106 | } 107 | } 108 | 109 | ReactDOM.render( 110 | , 111 | document.getElementById('root') 112 | ); 113 | 114 | 115 | -------------------------------------------------------------------------------- /labs/lab-react-wiki-countries/README.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | WikiCountries 4 | 5 | ## Introduction 6 | 7 | After spending too much time on GitHub, you found a [JSON database of countries](https://github.com/mledoze/countries/blob/master/countries.json) and you decide to use it to create your Wikipedia for countries! 8 | 9 | ![](https://media.giphy.com/media/fdUHHKI36bTVduRDfB/giphy.gif) 10 | 11 | 12 | ## Installation 13 | 14 | ### Setup a basic project 15 | Commands to launch 16 | ```sh 17 | $ npm install -g create-react-app # Install globally the `create-react-app` command 18 | $ create-react-app my-app # Create a React project folder "my-app" 19 | $ cd my-app 20 | $ npm install --save react-router-dom 21 | $ rm -f src/* 22 | $ touch src/index.js src/style.css # Create 2 files 23 | ``` 24 | 25 | Your `src/index.js` file 26 | ```javascript 27 | import React from 'react'; 28 | import ReactDOM from 'react-dom'; 29 | import { BrowserRouter } from 'react-router-dom' 30 | import './style.css'; 31 | 32 | class App extends React.Component { 33 | render() { 34 | return ( 35 |
    36 | {/* Your application code */} 37 |
    38 | ); 39 | } 40 | } 41 | 42 | ReactDOM.render(( 43 | 44 | 45 | 46 | ), 47 | document.getElementById('root') 48 | ); 49 | 50 | ``` 51 | 52 | To help you, we gave you an example of page inside [`example.html`](example.html) 53 | 54 | ### Bootstrap installation 55 | 56 | We will use [Twitter Bootstrap V4](https://getbootstrap.com/) for the design :) 57 | 58 | ```sh 59 | $ npm install bootstrap --save 60 | ``` 61 | 62 | ```javascript 63 | // src/index.js 64 | import 'bootstrap/dist/css/bootstrap.css'; 65 | ``` 66 | 67 | 68 | ## Instructions 69 | 70 | ### Iteration 1 | Create the components 71 | 72 | In this iteration, we will focus on general layout. You will create at least 2 components: 73 | - `App`: For the general layout 74 | - `CountryDetail`: A component that will receive `cca3` as a props, that is going to respresent the id of the country (example: `ESP` for Spain, `FRA` for France). 75 | 76 | To help you, you can use: 77 | - `example.html`: An example of what you can render 78 | - `countries.json`: The JSON database of countries. It's an array of object where each object represents a country and the property `cca3` is unique and will be used as the key to navigate. 79 | 80 | 81 | As a reminder with Twitter Bootstrap: 82 | ```html 83 | 84 |
    85 |
    Column 5/12
    86 |
    Column 7/12
    87 |
    88 | 89 | 90 | 97 | ``` 98 | 99 | 100 | ### Iteration 2 | Create the all application 101 | 102 | Everything is in the title. Good luck :) 103 | 104 | 105 | 106 | 107 | -------------------------------------------------------------------------------- /labs/lab-react-wiki-countries/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 |
    11 |
    12 | 17 |
    18 |
    19 | 38 |
    39 |

    France

    40 | 41 | 42 | 43 | 44 | 45 | 46 | 47 | 48 | 49 | 52 | 53 | 54 | 55 | 67 | 68 | 69 |
    CapitalParis
    Area551695 km 50 | 2 51 |
    Borders 56 | 66 |
    70 |
    71 |
    72 |
    73 |
    74 |
    75 | -------------------------------------------------------------------------------- /lessons/extra-react-bootstrap-and-scss.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | Bootstrap and SCSS 4 | 5 | ## Introduction 6 | 7 | In this course, we will learn 2 things: 8 | - How to use a React Component library for Bootstrap called reactstrap 9 | - How to do some SCSS 10 | 11 | 12 | ## [Reactstrap](https://reactstrap.github.io/) 13 | 14 | ### Installation 15 | 16 | ```sh 17 | # If you are in the MERN boilerplate 18 | $ cd client 19 | 20 | # In every case 21 | $ npm install --save reactstrap bootstrap 22 | ``` 23 | 24 | 25 | Import Bootstrap CSS, for example in the `client/src/index.js` file: 26 | 27 | ```js 28 | import 'bootstrap/dist/css/bootstrap.min.css'; 29 | ``` 30 | 31 | ### Include a Button 32 | 33 | If you want to display a Button in a component, you can do something like this 34 | 35 | ```js 36 | // At the beginning of the file 37 | import { Button } from 'reactstrap'; 38 | ``` 39 | 40 | ```js 41 | // In the render 42 | 43 | ``` 44 | 45 | 46 | ### Other Components 47 | 48 | You will find more on the official documentation, such as: 49 | - [Alert](https://reactstrap.github.io/components/alerts/) 50 | - [Navbar](https://reactstrap.github.io/components/navbar/) (it will let you collapsing your Navbar with a props) 51 | 52 | 53 | ## SCSS with React 54 | 55 | Create a `client/src/styles/` folder. We will listen for SCSS files inside it and transform them into CSS files. 56 | 57 | Then install some packages. 58 | 59 | ```sh 60 | # If you are in the MERN boilerplate 61 | $ cd client 62 | 63 | # In every case 64 | $ npm install --save node-sass-chokidar npm-run-all bootstrap 65 | ``` 66 | 67 | Add the new tasks in the `package.json` 68 | 69 | ``` 70 | "scripts": { 71 | "build-css": "node-sass-chokidar --include-path ./src/styles --include-path ./node_modules src/styles/ -o src/styles/", 72 | "watch-css": "npm run build-css && node-sass-chokidar --include-path ./src/styles --include-path ./node_modules src/styles/ -o src/styles --watch --recursive", 73 | "start-js": "react-scripts start", 74 | "start": "npm-run-all -p watch-css start-js", 75 | "build": "npm run build-css && react-scripts build", 76 | "test": "react-scripts test --env=jsdom", 77 | "eject": "react-scripts eject" 78 | } 79 | ``` 80 | 81 | As you can see if you look at your `package.json` this are the new commands: 82 | - `build-css`: Builds the css from the /src/styles/ directory. 83 | - `watch-css`: Whatches for changes of the .scss files from the /src/styles/ directory. 84 | - `start-js`: Executes the old start command. 85 | - `start`: Runs in parallel whatch-css and start-js. 86 | - `build`: Builds the css and build the scripts. 87 | 88 | 89 | Then you can create a file `src/styles/style.scss` that look like this: 90 | ```scss 91 | // Your new color: here the primary color is a dark blue 92 | $primary: #425cbb; 93 | // $secondary 94 | // $success 95 | // $info 96 | // $warning 97 | // $danger 98 | // $light 99 | // $dark 100 | 101 | @import 'bootstrap/scss/bootstrap'; 102 | 103 | // You can put your own SCSS here 104 | ``` 105 | 106 | Now, when you run your React application, you will have Bootstrap on your website with your own variables (different colors). 107 | 108 | Be careful, you should not include the original `bootstrap` in other files, such as `src/index.js`. 109 | 110 | In the end, you can import your `src/styles/style.css` file in `src/index.js`: 111 | ```js 112 | import './styles/style.css'; 113 | ``` 114 | 115 | 116 | ## Resources 117 | - [Reactstrap](https://reactstrap.github.io/) 118 | - [React + Custom Bootstrap 4 Sass tutorial](https://dev.to/sabatesduran/add-custom-bootstrap-4-sass-to-create-react-app) 119 | -------------------------------------------------------------------------------- /lessons/2-2-react-handling-events-and-conditional-rendering.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | Handling Events & Conditional Rendering 4 | 5 | 6 | ## [React | 6. Handling Events](https://reactjs.org/docs/handling-events.html) 7 | 8 | 9 | Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences: 10 | 11 | - React events are named using camelCase 12 | - With JSX you pass a function as the event handler 13 | 14 | **Examples** 15 | ```jsx 16 | 19 | ``` 20 | 21 | ```jsx 22 | {/* Call of the deleteRow method */} 23 | 24 | 25 | {/* The same with binding */} 26 | 27 | ``` 28 | 29 | ```javascript 30 | class Toggle extends React.Component { 31 | constructor(props) { 32 | super(props); 33 | this.state = {isToggleOn: true}; 34 | 35 | // This binding is necessary to make `this` work in the callback 36 | this.handleClick = this.handleClick.bind(this); 37 | } 38 | 39 | handleClick() { 40 | this.setState(prevState => ({ 41 | isToggleOn: !prevState.isToggleOn 42 | })); 43 | } 44 | 45 | render() { 46 | return ( 47 | 50 | ); 51 | } 52 | } 53 | ``` 54 | 55 | #### List of events 56 | 57 | Event Types | Event Names 58 | -- | -- 59 | Clipboard Events | `onCopy` `onCut` `onPaste` 60 | Composition Events | `onCompositionEnd` `onCompositionStart` `onCompositionUpdate` 61 | Keyboard Events | `onKeyDown` `onKeyPress` `onKeyUp` 62 | Focus Events | `onFocus` `onBlur` 63 | Form Events | `onChange` `onInput` `onInvalid` `onSubmit` 64 | Mouse Events | `onClick` `onContextMenu` `onDoubleClick` `onDrag` `onDragEnd` `onDragEnter` `onDragExit` `onDragLeave` `onDragOver` `onDragStart` `onDrop` `onMouseDown` `onMouseEnter` `onMouseLeave` `onMouseMove` `onMouseOut` `onMouseOver` `onMouseUp` 65 | Selection Events | `onSelect` 66 | Touch Events | `onTouchCancel` `onTouchEnd` `onTouchMove` `onTouchStart` 67 | UI Events | `onScroll` 68 | Wheel Events | `onWheel` 69 | Media Events | `onAbort` `onCanPlay` `onCanPlayThrough` `onDurationChange` `onEmptied` `onEncrypted` `onEnded` `onError` `onLoadedData` `onLoadedMetadata` `onLoadStart` `onPause` `onPlay` `onPlaying` `onProgress` `onRateChange` `onSeeked` `onSeeking` `onStalled` `onSuspend` `onTimeUpdate` `onVolumeChange` `onWaiting` 70 | Image Events | `onLoad` `onError` 71 | Animation Events | `onAnimationStart` `onAnimationEnd` `onAnimationIteration` 72 | Transition Events | `onTransitionEnd` 73 | Other Events | `onToggle` 74 | 75 | 76 | 77 | ## [React | 7. Conditional Rendering](https://reactjs.org/docs/conditional-rendering.html) 78 | 79 | 80 | ```javascript 81 | class MyComponent extends React.Component { 82 | showButton() { 83 | if (this.props.isLoggedIn) 84 | return 85 | else 86 | return 87 | } 88 | render() { 89 | let button 90 | if (this.props.isLoggedIn) 91 | button = 92 | else 93 | button = 94 | return ( 95 |
    96 | {/********** Method 1: Variable **********/} 97 | {button} 98 | {/********** Method 2: Function **********/} 99 | {this.showButton()} 100 | {/********** Method 3: Ternary **********/} 101 | {this.props.isLoggedIn ? : } 102 | {/********** Method 4: Inline If with Logical && Operator **********/} 103 | {this.props.isLoggedIn && } 104 | {!this.props.isLoggedIn && } 105 |
    106 | ) 107 | } 108 | } 109 | ``` 110 | 111 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/README.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React | IronNutrition 4 | 5 | ## Introduction 6 | 7 | Since the beginning of the bootcamp, you just realized that your diet is not healthy and it may have an impact on your health (and productivity) and the long term. 8 | 9 | To take care about the food you eat, you decided to create a nutrition app that will track everything you eat! 10 | 11 | 12 | ![](https://media.giphy.com/media/fH0dyqpPJRvTbiF5rJ/giphy.gif) 13 | 14 | ## Installation 15 | 16 | ### Launch the starter-code 17 | 18 | ``` 19 | $ cd starter-code 20 | $ npm install 21 | $ npm start 22 | ``` 23 | 24 | 25 | ### Bulma installation 26 | 27 | We will use [Bulma](https://bulma.io/) for the design :) 28 | 29 | ```sh 30 | $ npm install bulma --save 31 | ``` 32 | 33 | ```javascript 34 | // src/index.js 35 | import 'bulma/css/bulma.css'; 36 | ``` 37 | 38 | 39 | 40 | ### Import a JSON 41 | 42 | You will need to save into the variable `foods` the result of `./foods.json` 43 | ```js 44 | import foods from './foods.json' 45 | ``` 46 | 47 | 48 | ## About the design 49 | 50 | If you struggle with the design, you can find a static example of what is expected inside [example.html](example.html) 51 | 52 | 53 | ## Instructions 54 | 55 | ### Iteration 1 | Create `FoodBox` component 56 | 57 | Create a `FoodBox` component that takes at least `food` as a prop and display a box with all information about an ingredient. 58 | 59 | We recommand to use that HTML to display properly the `FoodBox`: 60 | 61 | ```html 62 |
    63 |
    64 |
    65 |
    66 | 67 |
    68 |
    69 |
    70 |
    71 |

    72 | Pizza
    73 | 400 cal 74 |

    75 |
    76 |
    77 |
    78 |
    79 |
    80 | 85 |
    86 |
    87 | 90 |
    91 |
    92 |
    93 |
    94 |
    95 | ``` 96 | 97 | ![](https://i.imgur.com/bY9i5Rw.png) 98 | 99 | 100 | ### Iteration 2 | Display food 101 | 102 | In your `App` component (your main component), display as many `FoodBox` as elements inside the variable `foods`. 103 | 104 | 105 | ![](https://i.imgur.com/3TVQJDO.png) 106 | 107 | 108 | ### Iteration 3 | Implement search bar 109 | 110 | Create a `Search` component to perform a search that updates the list of all meal. 111 | 112 | ![](https://i.imgur.com/XaOpAx8.png) 113 | 114 | 115 | 116 | ### Iteration 4 | Create add buttons 117 | 118 | On your `FoodBox`, you have an input an "+" button. Use them so that when a user click on the button, it adds them on a list on the right called "*Today's foods*". 119 | 120 | You will also need to display the total amount of calories at the bottom of the list as a recap. 121 | 122 | ![](https://media.giphy.com/media/fH0dyqpPJRvTbiF5rJ/giphy.gif) 123 | 124 | If you don't remember how to create responsive columns with Bulma, you can check the [documentation](https://bulma.io/documentation/columns/basics/). 125 | 126 | 127 | ### Iteration 5 | Bonus | Group ingredients 128 | 129 | You made an awesome application, but you have found a little problem in the UX. For example, if you click twice on "Pizza", it will display 2 lines "*1 Pizza = 400 cal*" instead of 1 line "*2 Pizza = 800 cal*". Fix that problem. 130 | 131 | 132 | ### Iteration 6 | Bonus | Allow the user to remove an ingredient 133 | 134 | On the "*Today's food*", add a trash icon to let users removing one of their item. 135 | 136 | 137 | 138 | ## Solution 139 | 140 | You will find the solution here: https://github.com/mc100s/training-labs-react/blob/master/src/lab-react-data-binding/solution.js 141 | -------------------------------------------------------------------------------- /labs/lab-axios-functional-programming/starter-code/expectations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 |

    Actors manipulations

    9 | 10 |

    total_results

    11 |
    18966
    12 | 13 |

    1st result name

    14 |
    "Chris Pratt"
    15 | 16 |

    Names of actors

    17 |
    [
     18 |   "Chris Pratt",
     19 |   "Bryce Dallas Howard",
     20 |   "Rose Byrne",
     21 |   "Henry Cavill",
     22 |   "Chris Hemsworth",
     23 |   "Carla Gugino",
     24 |   "Alicia Vikander",
     25 |   "Jennifer Lawrence",
     26 |   "Robert Downey Jr.",
     27 |   "Jeff Goldblum",
     28 |   "Sean Bean",
     29 |   "Dwayne Johnson",
     30 |   "Jennifer Jason Leigh",
     31 |   "Alexandra Daddario",
     32 |   "Scarlett Johansson",
     33 |   "Margot Robbie",
     34 |   "Beyoncé Knowles",
     35 |   "Gina Gershon",
     36 |   "Josh Brolin",
     37 |   "Bradley Cooper"
     38 | ]
    39 | 40 |

    Ids and names of actors

    41 |
    [
     42 |   "#73457 Chris Pratt",
     43 |   "#18997 Bryce Dallas Howard",
     44 |   "#9827 Rose Byrne",
     45 |   "#73968 Henry Cavill",
     46 |   "#74568 Chris Hemsworth",
     47 |   "#17832 Carla Gugino",
     48 |   "#227454 Alicia Vikander",
     49 |   "#72129 Jennifer Lawrence",
     50 |   "#3223 Robert Downey Jr.",
     51 |   "#4785 Jeff Goldblum",
     52 |   "#48 Sean Bean",
     53 |   "#18918 Dwayne Johnson",
     54 |   "#10431 Jennifer Jason Leigh",
     55 |   "#109513 Alexandra Daddario",
     56 |   "#1245 Scarlett Johansson",
     57 |   "#234352 Margot Robbie",
     58 |   "#14386 Beyoncé Knowles",
     59 |   "#11150 Gina Gershon",
     60 |   "#16851 Josh Brolin",
     61 |   "#51329 Bradley Cooper"
     62 | ]
    63 | 64 |

    Names of actors, sorted by name

    65 |
    [
     66 |   "Alexandra Daddario",
     67 |   "Alicia Vikander",
     68 |   "Beyoncé Knowles",
     69 |   "Bradley Cooper",
     70 |   "Bryce Dallas Howard",
     71 |   "Carla Gugino",
     72 |   "Chris Hemsworth",
     73 |   "Chris Pratt",
     74 |   "Dwayne Johnson",
     75 |   "Gina Gershon",
     76 |   "Henry Cavill",
     77 |   "Jeff Goldblum",
     78 |   "Jennifer Jason Leigh",
     79 |   "Jennifer Lawrence",
     80 |   "Josh Brolin",
     81 |   "Margot Robbie",
     82 |   "Robert Downey Jr.",
     83 |   "Rose Byrne",
     84 |   "Scarlett Johansson",
     85 |   "Sean Bean"
     86 | ]
    87 | 88 |

    Names of actors, that has "m" in their name

    89 |
    [
     90 |   "Chris Hemsworth",
     91 |   "Jeff Goldblum",
     92 |   "Margot Robbie"
     93 | ]
    94 | 95 |

    Names of actors, with their "know_for" movies

    96 |
    [
     97 |   "Chris Pratt (Guardians of the Galaxy, Jurassic World, Guardians of the Galaxy Vol. 2)",
     98 |   "Bryce Dallas Howard (Jurassic World, Spider-Man 3, The Twilight Saga: Eclipse)",
     99 |   "Rose Byrne (X-Men: First Class, X-Men: Apocalypse, Star Wars: Episode II - Attack of the Clones)",
    100 |   "Henry Cavill (Batman v Superman: Dawn of Justice, Man of Steel, Justice League)",
    101 |   "Chris Hemsworth (The Avengers, Avengers: Age of Ultron, Thor)",
    102 |   "Carla Gugino (Batman v Superman: Dawn of Justice, Man of Steel, Night at the Museum)",
    103 |   "Alicia Vikander (Ex Machina, Jason Bourne, The Man from U.N.C.L.E.)",
    104 |   "Jennifer Lawrence (The Hunger Games, The Hunger Games: Catching Fire, X-Men: Days of Future Past)",
    105 |   "Robert Downey Jr. (The Avengers, Iron Man, Iron Man 3)",
    106 |   "Jeff Goldblum (Guardians of the Galaxy Vol. 2, Jurassic Park, Thor: Ragnarok)",
    107 |   "Sean Bean (The Lord of the Rings: The Fellowship of the Ring, The Lord of the Rings: The Return of the King, The Lord of the Rings: The Two Towers)",
    108 |   "Dwayne Johnson (Fast & Furious 6, Furious 7, The Fate of the Furious)",
    109 |   "Jennifer Jason Leigh (The Hateful Eight, Annihilation, The Machinist)",
    110 |   "Alexandra Daddario (San Andreas, Baywatch, Percy Jackson & the Olympians: The Lightning Thief)",
    111 |   "Scarlett Johansson (The Avengers, Captain America: Civil War, Avengers: Age of Ultron)",
    112 |   "Margot Robbie (Suicide Squad, The Wolf of Wall Street, The Big Short)",
    113 |   "Beyoncé Knowles (Epic, Austin Powers in Goldmember, The Pink Panther)",
    114 |   "Gina Gershon (Face/Off, P.S. I Love You, LOL)",
    115 |   "Josh Brolin (Guardians of the Galaxy, Avengers: Age of Ultron, Men in Black 3)",
    116 |   "Bradley Cooper (Guardians of the Galaxy, Guardians of the Galaxy Vol. 2, The Hangover)"
    117 | ]
    118 | 119 | -------------------------------------------------------------------------------- /labs-solutions/lab-axios-functional-programming/solution-code/expectations.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | Document 6 | 7 | 8 |

    Actors manipulations

    9 | 10 |

    total_results

    11 |
    18966
    12 | 13 |

    1st result name

    14 |
    "Chris Pratt"
    15 | 16 |

    Names of actors

    17 |
    [
     18 |   "Chris Pratt",
     19 |   "Bryce Dallas Howard",
     20 |   "Rose Byrne",
     21 |   "Henry Cavill",
     22 |   "Chris Hemsworth",
     23 |   "Carla Gugino",
     24 |   "Alicia Vikander",
     25 |   "Jennifer Lawrence",
     26 |   "Robert Downey Jr.",
     27 |   "Jeff Goldblum",
     28 |   "Sean Bean",
     29 |   "Dwayne Johnson",
     30 |   "Jennifer Jason Leigh",
     31 |   "Alexandra Daddario",
     32 |   "Scarlett Johansson",
     33 |   "Margot Robbie",
     34 |   "Beyoncé Knowles",
     35 |   "Gina Gershon",
     36 |   "Josh Brolin",
     37 |   "Bradley Cooper"
     38 | ]
    39 | 40 |

    Ids and names of actors

    41 |
    [
     42 |   "#73457 Chris Pratt",
     43 |   "#18997 Bryce Dallas Howard",
     44 |   "#9827 Rose Byrne",
     45 |   "#73968 Henry Cavill",
     46 |   "#74568 Chris Hemsworth",
     47 |   "#17832 Carla Gugino",
     48 |   "#227454 Alicia Vikander",
     49 |   "#72129 Jennifer Lawrence",
     50 |   "#3223 Robert Downey Jr.",
     51 |   "#4785 Jeff Goldblum",
     52 |   "#48 Sean Bean",
     53 |   "#18918 Dwayne Johnson",
     54 |   "#10431 Jennifer Jason Leigh",
     55 |   "#109513 Alexandra Daddario",
     56 |   "#1245 Scarlett Johansson",
     57 |   "#234352 Margot Robbie",
     58 |   "#14386 Beyoncé Knowles",
     59 |   "#11150 Gina Gershon",
     60 |   "#16851 Josh Brolin",
     61 |   "#51329 Bradley Cooper"
     62 | ]
    63 | 64 |

    Names of actors, sorted by name

    65 |
    [
     66 |   "Alexandra Daddario",
     67 |   "Alicia Vikander",
     68 |   "Beyoncé Knowles",
     69 |   "Bradley Cooper",
     70 |   "Bryce Dallas Howard",
     71 |   "Carla Gugino",
     72 |   "Chris Hemsworth",
     73 |   "Chris Pratt",
     74 |   "Dwayne Johnson",
     75 |   "Gina Gershon",
     76 |   "Henry Cavill",
     77 |   "Jeff Goldblum",
     78 |   "Jennifer Jason Leigh",
     79 |   "Jennifer Lawrence",
     80 |   "Josh Brolin",
     81 |   "Margot Robbie",
     82 |   "Robert Downey Jr.",
     83 |   "Rose Byrne",
     84 |   "Scarlett Johansson",
     85 |   "Sean Bean"
     86 | ]
    87 | 88 |

    Names of actors, that has "m" in their name

    89 |
    [
     90 |   "Chris Hemsworth",
     91 |   "Jeff Goldblum",
     92 |   "Margot Robbie"
     93 | ]
    94 | 95 |

    Names of actors, with their "know_for" movies

    96 |
    [
     97 |   "Chris Pratt (Guardians of the Galaxy, Jurassic World, Guardians of the Galaxy Vol. 2)",
     98 |   "Bryce Dallas Howard (Jurassic World, Spider-Man 3, The Twilight Saga: Eclipse)",
     99 |   "Rose Byrne (X-Men: First Class, X-Men: Apocalypse, Star Wars: Episode II - Attack of the Clones)",
    100 |   "Henry Cavill (Batman v Superman: Dawn of Justice, Man of Steel, Justice League)",
    101 |   "Chris Hemsworth (The Avengers, Avengers: Age of Ultron, Thor)",
    102 |   "Carla Gugino (Batman v Superman: Dawn of Justice, Man of Steel, Night at the Museum)",
    103 |   "Alicia Vikander (Ex Machina, Jason Bourne, The Man from U.N.C.L.E.)",
    104 |   "Jennifer Lawrence (The Hunger Games, The Hunger Games: Catching Fire, X-Men: Days of Future Past)",
    105 |   "Robert Downey Jr. (The Avengers, Iron Man, Iron Man 3)",
    106 |   "Jeff Goldblum (Guardians of the Galaxy Vol. 2, Jurassic Park, Thor: Ragnarok)",
    107 |   "Sean Bean (The Lord of the Rings: The Fellowship of the Ring, The Lord of the Rings: The Return of the King, The Lord of the Rings: The Two Towers)",
    108 |   "Dwayne Johnson (Fast & Furious 6, Furious 7, The Fate of the Furious)",
    109 |   "Jennifer Jason Leigh (The Hateful Eight, Annihilation, The Machinist)",
    110 |   "Alexandra Daddario (San Andreas, Baywatch, Percy Jackson & the Olympians: The Lightning Thief)",
    111 |   "Scarlett Johansson (The Avengers, Captain America: Civil War, Avengers: Age of Ultron)",
    112 |   "Margot Robbie (Suicide Squad, The Wolf of Wall Street, The Big Short)",
    113 |   "Beyoncé Knowles (Epic, Austin Powers in Goldmember, The Pink Panther)",
    114 |   "Gina Gershon (Face/Off, P.S. I Love You, LOL)",
    115 |   "Josh Brolin (Guardians of the Galaxy, Avengers: Age of Ultron, Men in Black 3)",
    116 |   "Bradley Cooper (Guardians of the Galaxy, Guardians of the Galaxy Vol. 2, The Hangover)"
    117 | ]
    118 | 119 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/solution.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import 'bulma/css/bulma.css'; 4 | 5 | import './style.css'; 6 | import foods from './foods.js' 7 | 8 | class Search extends React.Component { 9 | render() { 10 | return ( 11 |
    12 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | class FoodBox extends React.Component { 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | qty: 1 26 | } 27 | } 28 | 29 | handleChange(event) { 30 | let v = event.target.value 31 | this.setState({qty: v}) 32 | } 33 | 34 | render() { 35 | return ( 36 |
    37 |
    38 |
    39 |
    40 | 41 |
    42 |
    43 |
    44 |
    45 |

    46 | {this.props.food.name}
    47 | {this.props.food.calories} cal 48 |

    49 |
    50 |
    51 |
    52 |
    53 |
    54 | {this.handleChange(event)}} 59 | /> 60 |
    61 |
    62 | 68 |
    69 |
    70 |
    71 |
    72 |
    73 | ) 74 | } 75 | } 76 | 77 | class App extends React.Component { 78 | constructor(props) { 79 | super(props); 80 | this.state = { 81 | search: "", 82 | todaysFoods: [] 83 | } 84 | } 85 | 86 | handleClick(food, qty) { 87 | let todaysFoods = this.state.todaysFoods.slice(); 88 | todaysFoods.push({...food, qty}); 89 | this.setState({ 90 | todaysFoods 91 | }) 92 | } 93 | 94 | displayFoods(search) { 95 | search = search.toUpperCase(); 96 | let result = []; 97 | for (let i = 0; i < foods.length; i++) { 98 | if (foods[i].name.toUpperCase().indexOf(search) !== -1) { 99 | result.push( 100 | 101 | ) 102 | } 103 | } 104 | return result; 105 | } 106 | 107 | displaySelectedFoods() { 108 | let result = []; 109 | for (let i = 0; i < this.state.todaysFoods.length; i++) { 110 | result.push( 111 |
  • {this.state.todaysFoods[i].qty} {this.state.todaysFoods[i].name} = {this.state.todaysFoods[i].qty * this.state.todaysFoods[i].calories} cal
  • 112 | ) 113 | } 114 | 115 | return result; 116 | } 117 | 118 | displaySelectedFoodsTotCal() { 119 | let result = 0; 120 | for (let i = 0; i < this.state.todaysFoods.length; i++) { 121 | result += this.state.todaysFoods[i].qty * this.state.todaysFoods[i].calories; 122 | } 123 | return result; 124 | } 125 | 126 | render() { 127 | return ( 128 |
    129 |

    IronNutrition

    130 | {this.setState({search: e.target.value})}} /> 131 |
    132 |
    133 | {this.displayFoods(this.state.search)} 134 |
    135 |
    136 |

    Today's foods

    137 |
      138 | {this.displaySelectedFoods()} 139 |
    140 | Total: {this.displaySelectedFoodsTotCal()} cal 141 |
    142 |
    143 |
    144 | ); 145 | } 146 | } 147 | 148 | ReactDOM.render( 149 | , 150 | document.getElementById('root') 151 | ); -------------------------------------------------------------------------------- /labs-solutions/lab-react-data-binding/solution-code/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import 'bulma/css/bulma.css'; 4 | 5 | import './style.css'; 6 | import foods from './foods.json' 7 | 8 | class Search extends React.Component { 9 | render() { 10 | return ( 11 |
    12 | 16 |
    17 | ) 18 | } 19 | } 20 | 21 | class FoodBox extends React.Component { 22 | constructor(props) { 23 | super(props); 24 | this.state = { 25 | qty: 1 26 | } 27 | } 28 | 29 | handleChange(event) { 30 | let v = event.target.value 31 | this.setState({qty: v}) 32 | } 33 | 34 | render() { 35 | return ( 36 |
    37 |
    38 |
    39 |
    40 | 41 |
    42 |
    43 |
    44 |
    45 |

    46 | {this.props.food.name}
    47 | {this.props.food.calories} cal 48 |

    49 |
    50 |
    51 |
    52 |
    53 |
    54 | {this.handleChange(event)}} 59 | /> 60 |
    61 |
    62 | 68 |
    69 |
    70 |
    71 |
    72 |
    73 | ) 74 | } 75 | } 76 | 77 | class App extends React.Component { 78 | constructor(props) { 79 | super(props); 80 | this.state = { 81 | search: "", 82 | todaysFoods: [] 83 | } 84 | } 85 | 86 | handleClick(food, qty) { 87 | let todaysFoods = this.state.todaysFoods.slice(); 88 | todaysFoods.push({...food, qty}); 89 | this.setState({ 90 | todaysFoods 91 | }) 92 | } 93 | 94 | displayFoods(search) { 95 | search = search.toUpperCase(); 96 | let result = []; 97 | for (let i = 0; i < foods.length; i++) { 98 | if (foods[i].name.toUpperCase().indexOf(search) !== -1) { 99 | result.push( 100 | 101 | ) 102 | } 103 | } 104 | return result; 105 | } 106 | 107 | displaySelectedFoods() { 108 | let result = []; 109 | for (let i = 0; i < this.state.todaysFoods.length; i++) { 110 | result.push( 111 |
  • {this.state.todaysFoods[i].qty} {this.state.todaysFoods[i].name} = {this.state.todaysFoods[i].qty * this.state.todaysFoods[i].calories} cal
  • 112 | ) 113 | } 114 | 115 | return result; 116 | } 117 | 118 | displaySelectedFoodsTotCal() { 119 | let result = 0; 120 | for (let i = 0; i < this.state.todaysFoods.length; i++) { 121 | result += this.state.todaysFoods[i].qty * this.state.todaysFoods[i].calories; 122 | } 123 | return result; 124 | } 125 | 126 | render() { 127 | return ( 128 |
    129 |

    IronNutrition

    130 | {this.setState({search: e.target.value})}} /> 131 |
    132 |
    133 | {this.displayFoods(this.state.search)} 134 |
    135 |
    136 |

    Today's foods

    137 |
      138 | {this.displaySelectedFoods()} 139 |
    140 | Total: {this.displaySelectedFoodsTotCal()} cal 141 |
    142 |
    143 |
    144 | ); 145 | } 146 | } 147 | 148 | ReactDOM.render( 149 | , 150 | document.getElementById('root') 151 | ); -------------------------------------------------------------------------------- /lessons/5-2-react-express-integration.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React & Express | Frontend Backend Integration 4 | 5 | ## [Summary of the previous course](http://learn.ironhack.com/#/learning_unit/2507) 6 | 7 | In the previous course, we've created our own REST API. For that, we've created an Express project where we: 8 | - Removed everything related to the views to sent JSON instead 9 | - Added `cors` 10 | 11 | In term of routes, we have: 12 | - `GET /api/phones` 13 | - `POST /api/phones` 14 | - `GET /api/phones/:id` 15 | - `PUT /api/phones/:id` 16 | - `DELETE /api/phones/:id` 17 | 18 | ## Add of React in the project 19 | 20 | In our Express app, we will insert the React code in a folder `/client` 21 | 22 | For that, generate the React app inside the Express app directory: 23 | ```sh 24 | $ create-react-app client 25 | $ npm install --save axios 26 | ``` 27 | 28 | ## Modify the React app 29 | 30 | 31 | 32 | We will create a `client/src/api.js` 33 | 34 | ```js 35 | // client/src/api.js 36 | import axios from 'axios'; 37 | 38 | const service = axios.create({ 39 | baseURL: `http://localhost:3000/api/` 40 | }); 41 | 42 | const errHandler = err => { 43 | console.error(err.response.data); 44 | throw err.response.data; 45 | }; 46 | 47 | export default { 48 | service: service, 49 | 50 | getPhones() { 51 | return service.get('phones') 52 | .then(res => res.data) 53 | .catch(errHandler); 54 | }, 55 | 56 | getPhoneDetail(id) { 57 | return service.get('phones/' + id) 58 | .then(res => res.data) 59 | .catch(errHandler); 60 | }, 61 | 62 | addPhone(data) { 63 | return service.post('phones', data) 64 | .then(res => res.data) 65 | .catch(errHandler); 66 | }, 67 | 68 | modifyPhone(id, data) { 69 | return service.post('phones/'+id, data) 70 | .then(res => res.data) 71 | .catch(errHandler); 72 | }, 73 | 74 | deletePhone(id) { 75 | return service.delete('phones/' + id) 76 | .then(res => res.data) 77 | .catch(errHandler); 78 | }, 79 | }; 80 | ``` 81 | 82 | Then, we can for example use `getPhones()` like this: 83 | 84 | ```js 85 | import api from './api' 86 | 87 | api.getPhones() 88 | .then(data => { 89 | console.log("This is my data:", data) 90 | }) 91 | ``` 92 | 93 | ## Test the application 94 | 95 | To test the application, you should have 2 terminals opened 96 | ```sh 97 | # Terminal 1 98 | $ npm start # or npm run dev 99 | ``` 100 | 101 | ```sh 102 | # Terminal 2 103 | $ cd client 104 | $ npm start 105 | ``` 106 | 107 | ## Example of a `ListPhones` component 108 | 109 | ```js 110 | import React, { Component } from 'react' 111 | import api from './api' 112 | 113 | class ListPhones extends Component { 114 | constructor(props) { 115 | super(props) 116 | this.state = { 117 | phones: [] 118 | } 119 | } 120 | componentDidMount() { 121 | api.getPhones() 122 | .then(data => { 123 | this.setState({ 124 | phones: data 125 | }) 126 | }) 127 | } 128 | render() { 129 | return ( 130 |
    131 |
      132 | {this.state.phones.map(phone =>
    • {phone.name}
    • )} 133 |
    134 |
    135 | ) 136 | } 137 | } 138 | 139 | export default ListPhones 140 | ``` 141 | 142 | 143 | #### Exercise 144 | 145 | Create a component with an number input that displays the phone with the id precised in the input. 146 | 147 | 148 | ## Package the app 149 | 150 | 151 | First, inside the `client` folder, let's run the following command: 152 | ```sh 153 | $ npm run build 154 | ``` 155 | 156 | It has just created a folder `client/build` that contains some HTML, CSS and JS files readable by a browser. 157 | 158 | 159 | ## Configure the Express server 160 | 161 | Now that we have our client application bundled in the `/client/build` folder we can move it into a static folder of our back-end app. We want our Express server to serve the entire client application. 162 | 163 | 164 | For that, we should add the following line in `app.js` 165 | 166 | ```js 167 | app.use(express.static(path.join(__dirname, 'client/build'))); 168 | 169 | // ... 170 | // The routes for the API 171 | // ... 172 | 173 | // This will be the default route is nothing else is caught 174 | app.use(function(req, res) { 175 | res.sendfile(__dirname + '/client/build/index.html'); 176 | }); 177 | ``` 178 | 179 | ## Summary 180 | 181 | In this lesson we have learned how to run a full-stack application, how to consume a REST API using Rest, and how to build an application for release. 182 | -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/README.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # IronContacts 4 | 5 | ## Introduction 6 | 7 | After Ironhack, you have decided to work in the movie industry and you've found a job where you need to manage the contacts of a famous producer. 8 | 9 | We are going to create contact management app with React for this producer. 10 | 11 | You can find the starter code in the starter code folder of this Github repo. 12 | 13 | ## Installation 14 | 15 | ``` 16 | $ cd starter-code 17 | $ npm install 18 | $ npm start 19 | ``` 20 | 21 | 22 | ## Instructions 23 | 24 | ### Iteration 1 | Display 5 Contacts 25 | 26 | Let's take a look at the starter code. 27 | 28 | There is a JSON file with the producer contacts, named `contacts.json`. Import it and display only the 5 first contacts in a `` and display the `picture`, `name`, and `popularity` of each contact. 29 | 30 | To import it, in the `src/index.js`, you can simply write: 31 | ```js 32 | import contacts from './contacts.json' 33 | ``` 34 | 35 | At the end of this iteration, your application should look like this: 36 | 37 | ![Screenshot](https://i.imgur.com/fPuwZXv.png) 38 | 39 | 40 | ### Iteration 2 | Add New Random Contacts 41 | 42 | In your application, create a "*Add Random Contact*, so that every time you click on this button, it adds a new random actor. 43 | 44 | At the end of this iteration, your website will probably look like this: 45 | 46 | ![Screenshot](https://i.imgur.com/GuNyYiU.png) 47 | 48 | 49 | ### Iteration 3 | Sort Contacts By Name And Popularity 50 | 51 | The producer asked you to add two new buttons to help him sorting his contacts. Create them so that when you click on it, it sorts the table on the `name` or `popularity` field. 52 | 53 | This is what you may have at the end of this iteration: 54 | 55 | ![Screenshot](https://i.imgur.com/vUDGZ7Y.png) 56 | 57 | 58 | ### Iteration 4 | Remove Contacts 59 | 60 | The producer also would like to have the opportunity to remove some of its contacts. Implement a "*Delete*" button on each row of your `
    ` that will let the user remove some contacts. 61 | 62 | At the end of this iteration, your web page may look like this after playing a little bit with the "*Delete*" buttons. 63 | 64 | ![Screenshot](https://i.imgur.com/N3K1K1k.png) 65 | 66 | 67 | ### Iteration 5 | Bonus | Styling 68 | 69 | Unfortunately, this contacts list isn't really production ready. So make it fancy! 70 | 71 | 72 | 73 | ## Hints 74 | 75 | Have a look at hints only if you are stuck for several minutes. All the code is encoded in Base64 and can be decoded here: https://www.base64decode.org/ 76 | 77 | ### Iteration 1 | Display 5 Contacts 78 | 79 | To display the first 5 contacts, first you can set a `contacts` state that will contain the list of all displayed contacts. Then you will need to loop on the variable `this.state.contacts` with a specific function 80 | 81 | ``` 82 | Y2xhc3MgQXBwIGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHsNCiAgY29uc3RydWN0b3IocHJvcHMpIHsNCiAgICBzdXBlcihwcm9wcyk7DQogICAgdGhpcy5zdGF0ZSA9IHsNCiAgICAgIGNvbnRhY3RzOiBjb250YWN0cy5zbGljZSgwLDUpDQogICAgfQ0KICB9DQogIA0KICBkaXNwbGF5Q29udGFjdHMoKSB7DQogICAgbGV0IHJlc3VsdCA9IFtdOw0KICAgIGZvciAobGV0IGkgPSAwOyBpIDwgdGhpcy5zdGF0ZS5jb250YWN0cy5sZW5ndGg7IGkrKykgew0KICAgICAgcmVzdWx0LnB1c2goDQogICAgICAgIDxDb250YWN0Um93DQogICAgICAgICAga2V5PXtpfSANCiAgICAgICAgICBjb250YWN0PXt0aGlzLnN0YXRlLmNvbnRhY3RzW2ldfSANCiAgICAgICAgLz4NCiAgICAgICkNCiAgICB9DQogICAgcmV0dXJuIHJlc3VsdDsNCiAgfQ0KDQogIHJlbmRlcigpIHsNCiAgICByZXR1cm4gKA0KICAgICAgPGRpdj4NCiAgICAgICAgPCEtLSAuLi4gLS0+DQogICAgICAgICAge3RoaXMuZGlzcGxheUNvbnRhY3RzKCl9DQogICAgICAgIDwhLS0gLi4uIC0tPg0KICAgICAgPC9kaXY+DQogICAgKTsNCiAgfQ0KfQ0KDQovLyBEb24ndCBmb3JnZXQgdG8gY3JlYXRlIGEgIkNvbnRhY3RSb3ciIGNvbXBvbmVudA== 83 | ``` 84 | 85 | ### Iteration 2 | Add New Random Contacts 86 | 87 | For this iteration, you will need to create a function `addRandomContact` that will update the state (with the method `this.setState`). In the following example, we decided to create a `AddRandomContactButton` component. 88 | 89 | ```js 90 | Y2xhc3MgQWRkUmFuZG9tQ29udGFjdEJ1dHRvbiBleHRlbmRzIFJlYWN0LkNvbXBvbmVudCB7DQogIHJlbmRlcigpIHsNCiAgICByZXR1cm4gKA0KICAgICAgPGJ1dHRvbiBvbkNsaWNrPXt0aGlzLnByb3BzLm9uQ2xpY2t9PkFkZCBSYW5kb20gQ29udGFjdDwvYnV0dG9uPg0KICAgICk7DQogIH0NCn0NCg0KY2xhc3MgQXBwIGV4dGVuZHMgUmVhY3QuQ29tcG9uZW50IHsNCiAgLy8gLi4uDQoNCiAgYWRkUmFuZG9tQ29udGFjdCgpIHsNCiAgICBsZXQgbmV3Q29udGFjdHMgPSB0aGlzLnN0YXRlLmNvbnRhY3RzLnNsaWNlKCk7DQogICAgbmV3Q29udGFjdHMucHVzaChjb250YWN0c1tNYXRoLmZsb29yKE1hdGgucmFuZG9tKCkqY29udGFjdHMubGVuZ3RoKV0pDQogICAgdGhpcy5zZXRTdGF0ZSh7DQogICAgICBjb250YWN0czogbmV3Q29udGFjdHMNCiAgICB9KQ0KICB9DQoNCiAgcmVuZGVyKCkgew0KICAgIHJldHVybiAoDQogICAgICAgIHsvKiAuLi4gKi99DQogICAgICAgIHsvKiBXaXRob3V0IGEgQ29tcG9uZW50ICovfQ0KICAgICAgICA8YnV0dG9uIG9uQ2xpY2s9eygpID0+IHt0aGlzLmFkZFJhbmRvbUNvbnRhY3QoKX19PkFkZCBSYW5kb20gQ29udGFjdDwvYnV0dG9uPg0KICAgICAgICB7LyogT1Igd2l0aCBhIENvbXBvbmVudCAqL30NCiAgICAgICAgPEFkZFJhbmRvbUNvbnRhY3RCdXR0b24gb25DbGljaz17KCkgPT4ge3RoaXMuYWRkUmFuZG9tQ29udGFjdCgpfX0gLz4NCiAgICAgICAgey8qIC4uLiAqL30NCiAgICApOw0KICB9DQp9 91 | ``` -------------------------------------------------------------------------------- /lessons/6-1-react-express-file-upload.md: -------------------------------------------------------------------------------- 1 | ![Ironhack Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React & Express | File Upload 4 | 5 | ## Introduction 6 | 7 | During that course, we will learn how to upload a file with the MERN stack and Cloudinary. 8 | 9 | ## Example with the MERN boilerplate 10 | 11 | ### Set up the backend 12 | 13 | Add your Cloudinary credentials. The values are accessible on https://cloudinary.com/console. 14 | 15 | **`server/.env`** 16 | ``` 17 | CLOUDINARY_CLOUD_NAME=........... 18 | CLOUDINARY_API_KEY=........... 19 | CLOUDINARY_API_SECRET=........... 20 | ``` 21 | 22 | Make sure you configured correctly Cloudinary (but it should be already good). 23 | 24 | **`server/configs/cloudinary.js`** 25 | ```js 26 | cloudinary.config({ 27 | cloud_name: process.env.CLOUDINARY_CLOUD_NAME, 28 | api_key: process.env.CLOUDINARY_API_KEY, 29 | api_secret: process.env.CLOUDINARY_API_SECRET 30 | }); 31 | ``` 32 | 33 | Make sure you require the `server/configs/cloudinary.js` inside **`server/app.js`** (but it should be the case). 34 | ```js 35 | // ... 36 | require('./configs/cloudinary'); 37 | // ... 38 | ``` 39 | 40 | In the file **`server/routes/users.js`**, you should see a `POST /api/users/first-user/pictures` route: 41 | ```js 42 | router.post('/first-user/pictures', parser.single('picture'), (req, res, next) => { 43 | User.findOneAndUpdate({}, { pictureUrl: req.file.url }) 44 | .then(() => { 45 | res.json({ 46 | success: true, 47 | pictureUrl: req.file.url 48 | }) 49 | }) 50 | }); 51 | ``` 52 | 53 | This enpoint is able to receive an input `picture` that is a file, 54 | 55 | 56 | 57 | ### Test with Postman 58 | 59 | This is the request you can do with Postman. After doing it, you should see a success JSON and you would be able to see with Mongo Compass that the first user of your database (probably `fullstack-countries`) has a pictureUrl. 60 | 61 | ![](https://i.imgur.com/lk5kue9.png) 62 | 63 | 64 | On Postman, if you click on *Code* (just bellow *Save*) and select *JavaScript > JavaScript Jqurey AJAX*, you will see an JavaScript example to simulate the same request. 65 | 66 | ![](https://i.imgur.com/zlCDYRI.png) 67 | 68 | With Axios it would be very similar. The most important part is the begining, we are going to reuse in React: 69 | 70 | ```js 71 | let form = new FormData(); 72 | form.append("picture", /* the file */); 73 | ``` 74 | 75 | 76 | ### Test with React 77 | 78 | Add a method in your `client/src/api.js` that will look like this: 79 | 80 | ```js 81 | addPicture(file) { 82 | const formData = new FormData(); 83 | formData.append("picture", file) 84 | console.log('DEBUG formData', formData.get("picture")); 85 | return service 86 | .post('/users/first-user/pictures', formData, { 87 | headers: { 88 | 'Content-Type': 'multipart/form-data', 89 | }, 90 | }) 91 | .then(res => res.data) 92 | .catch(errHandler); 93 | }, 94 | ``` 95 | 96 | Then, to test it in the Home Component, you can change the `client/src/components/Home.js` file: 97 | 98 | ```js 99 | import React, { Component } from 'react'; 100 | import api from '../api'; 101 | 102 | class Home extends Component { 103 | constructor(props) { 104 | super(props) 105 | this.state = { 106 | file: null 107 | } 108 | } 109 | handleChange(e) { 110 | console.log('handleChange'); 111 | console.log('DEBUG e.target.files[0]', e.target.files[0]); 112 | this.setState({ 113 | file: e.target.files[0] 114 | }) 115 | } 116 | handleSubmit(e) { 117 | e.preventDefault() 118 | api.addPicture(this.state.file) 119 | } 120 | render() { 121 | return ( 122 |
    123 |

    Home

    124 |

    This is a sample project with the MERN stack

    125 | 126 |
    this.handleSubmit(e)}> 127 | this.handleChange(e)} />
    128 | 129 | 130 |
    131 | ); 132 | } 133 | } 134 | 135 | export default Home; 136 | ``` 137 | 138 | Now, try the form with your React application. 139 | 140 | You should see on *Network* tab of Chrome that the request was succesful. 141 | 142 | ![](https://i.imgur.com/K8SiVhK.png) 143 | 144 | You should also see on your database that the first user has a field `pictureUrl`. 145 | ![](https://i.imgur.com/aJDz8K7.png) 146 | 147 | ## Exercise 148 | 149 | Create a `/profile` page in React with: 150 | - The basic information about the user: name, email and pictureUrl 151 | - A form to upload a new profile picture 152 | 153 | For that, you will need to create 154 | - A `Profile` component 155 | - A new route `POST /api/users/picture` 156 | 157 | 158 | ## Code sample 159 | 160 | ### Generic signup form with a file 161 | 162 | ```js 163 | export default { 164 | // ... 165 | signup(userInfo) { 166 | const formData = new FormData(); 167 | Object.keys(userInfo).forEach(key => formData.append(key, userInfo[key])); 168 | return service 169 | .post('/signup', formData, { 170 | headers: { 171 | 'Content-Type': 'multipart/form-data', 172 | }, 173 | }) 174 | .then(res => res.data) 175 | .catch(errHandler); 176 | }, 177 | // ... 178 | } 179 | ``` -------------------------------------------------------------------------------- /labs-solutions/lab-axios-functional-programming/README.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # Axios and functional programming 4 | 5 | ## Introduction 6 | 7 | The goal of this exercise is to play with the data given by a fake REST API about actors. 8 | 9 | ### The API 10 | 11 | We will play with a fake API that gives infromation about actors. 12 | 13 | This API has only 3 endpoints: 14 | - `GET /page-1.json` 15 | - `GET /page-2.json` 16 | - `GET /page-3.json` 17 | 18 | The base URL of this API is: https://raw.githubusercontent.com/mc100s/training-labs-react/master/src/lab-axios-functional-programming 19 | 20 | 21 | ### The files `api.js` and `dom-manipulation.js` 22 | 23 | `api.js` is the file where you will need to code your calls to the API. 24 | 25 | `dom-maninpulation.js` is the file that use `api.js` to manipulate the API. The only thing you can change in this file is the variable `page` at the top. 26 | 27 | 28 | ### Expectations 29 | 30 | You will find the example of what you should have in the file [`expectations.html`](starter-code/expectations.html) 31 | 32 | ![](https://i.imgur.com/qwwac64.png) 33 | 34 | ## Instructions 35 | 36 | ### Iteration 1 37 | 38 | Let's do this iteration together :) 39 | 40 | The goal is to code the function `getTotalResults` from the file `api.js`. This function is reused in `dom-manipulation.js`. 41 | 42 | First, understand what this function is doing. 43 | 44 | ```js 45 | // From api.js 46 | let service = axios.create({ 47 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 48 | }) 49 | 50 | // From api.js 51 | function getTotalResults(page) { 52 | return service.get(`page-${page}.json`) 53 | .then(response => { 54 | // TODO: Iteration 1 55 | return response.data 56 | }) 57 | } 58 | 59 | // From dom-manipulation.js 60 | let page = 1 61 | 62 | // From dom-manipulation.js 63 | getTotalResults(page) 64 | .then(updateTheDom("getTotalResults")) 65 | 66 | // From dom-manipulation.js 67 | function updateTheDom(elementId) { 68 | return (result) => { 69 | document.getElementById(elementId).innerHTML = JSON.stringify(result, null, 2) 70 | } 71 | } 72 | ``` 73 | 74 | If we replace `updateTheDom` by its return value, this is what we have: 75 | ```js 76 | let service = axios.create({ 77 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 78 | }) 79 | 80 | function getTotalResults(page) { 81 | return service.get(`page-${page}.json`) 82 | .then(response => { 83 | return response.data 84 | }) 85 | } 86 | 87 | let page = 1 88 | 89 | getTotalResults(page) 90 | .then(result => { 91 | document.getElementById("getTotalResults").innerHTML = JSON.stringify(result, null, 2) 92 | } 93 | }) 94 | ``` 95 | 96 | Now, if we replace `getTotalResults` by its return value, this is what we have: 97 | ```js 98 | let service = axios.create({ 99 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 100 | }) 101 | 102 | let page = 1 103 | 104 | service.get(`page-${page}.json`) 105 | .then(response => { 106 | return response.data 107 | }) 108 | .then(result => { 109 | document.getElementById("getTotalResults").innerHTML = JSON.stringify(result, null, 2) 110 | }) 111 | ``` 112 | 113 | Now it's easier to understand: 114 | 1. We use the API by calling `GET page-1.json` (you can do the same with your browser by going [here](https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/page-1.json)) 115 | 2. We give to the next promise the value `response.data`, that represents the JSON sent by the API 116 | 3. We select the DOM element with the ID `getTotalResults` and we set its content to the value we have. 117 | 118 | That's why if you haven't modified the code yet, you should see the all JSON on the `index.html` page. 119 | 120 | ![](https://i.imgur.com/rYsVbVd.png) 121 | 122 | 123 | Here, the only part that we want to display is the property `total_results` from the JSON. 124 | 125 | That's why we can code the `getTotalResults` like this: 126 | 127 | ```js 128 | function getTotalResults(page) { 129 | return service.get(`page-${page}.json`) 130 | .then(response => { 131 | return response.data.total_results 132 | }) 133 | } 134 | ``` 135 | 136 | ### Iteration 2 137 | 138 | In `api.js`, modify the code of `getFirstResultName` so it gives a promise with the name of the first actor. 139 | 140 | ![](https://i.imgur.com/Cx6OeOc.png) 141 | 142 | 143 | 144 | ### Iteration 3 145 | 146 | In `api.js`, modify the code of `getNames` so it gives a promise with the array of names of the actors. For that we recommand you to use a `map`. 147 | 148 | ![](https://i.imgur.com/ViCh1e3.png) 149 | 150 | 151 | 152 | ### Iteration 4 153 | 154 | In `api.js`, modify the code of `getIdsAndNames` so it gives a promise with the array of "ids and names" like in the following example. 155 | 156 | ![](https://i.imgur.com/jTps66E.png) 157 | 158 | 159 | 160 | ### Iteration 5 161 | 162 | In `api.js`, modify the code of `getSortedNames` so it gives a promise with the array of names sorted by lexicographical order. 163 | 164 | ![](https://i.imgur.com/9cjdJL6.png) 165 | 166 | 167 | ### Iteration 6 168 | 169 | In `api.js`, modify the code of `getNamesFiltered` so it gives a promise with the array of names that contains a `searchTerm`. Be careful, the search should be case insensitive. 170 | 171 | ![](https://i.imgur.com/JmodMh6.png) 172 | 173 | ### Iteration 7 (bonus) 174 | 175 | In `api.js`, modify the code of `getActorNamesWithTheirKnownForMovies` so it gives a promise with the array of "actor names and their know_for movies". 176 | 177 | ![](https://i.imgur.com/ul9TSyq.png) 178 | 179 | 180 | ### Iteration 8 (bonus) 181 | 182 | In `api.js`, create from scratch a function `getKnownForMovies` that will get all the `know_for` movies and find a way to display it in your HTML page. -------------------------------------------------------------------------------- /labs/lab-react-bulma-components/README.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React Components & Bulma 4 | 5 | ## Introduction 6 | 7 | Do you know [Bulma](https://bulma.io), a very nice alternative to Bootstrap as a CSS framework? We are going to create a simple website with Bulma and React! 8 | 9 | 10 | You can find the starter code in the starter code folder of this Github repo. 11 | 12 | 13 | ## Installation 14 | 15 | ### Setup a basic project 16 | Commands to launch 17 | ```sh 18 | $ npm install -g create-react-app # Install globally the `create-react-app` command 19 | $ create-react-app starter-code # Create a React project folder "starter-code" 20 | $ cd starter-code 21 | $ rm -f src/* 22 | $ touch src/index.js src/index.css # Create 2 files 23 | ``` 24 | 25 | Your `src/index.js` file 26 | ```javascript 27 | import React from 'react'; 28 | import ReactDOM from 'react-dom'; 29 | import './index.css'; 30 | 31 | class App extends React.Component { 32 | render() { 33 | return ( 34 |
    35 | {/* Your application code */} 36 |
    37 | ); 38 | } 39 | } 40 | 41 | ReactDOM.render( 42 | , 43 | document.getElementById('root') 44 | ); 45 | ``` 46 | 47 | ### Bulma installation 48 | ``` 49 | $ npm install --save bulma 50 | ``` 51 | 52 | ```javascript 53 | import 'bulma/css/bulma.css'; 54 | ``` 55 | 56 | 57 | ## Instructions 58 | 59 | ### Iteration 1 | `Button` Component 60 | 61 | The goal is to create a component called `Button` that creates a ` 68 | 69 | ``` 70 | ```html 71 | 72 | 73 | 74 | ``` 75 | 76 | What is visually rendered 77 | 78 | ![](https://i.imgur.com/qrfQG18.png) 79 | 80 | 81 | Because there are many cases to code, focus on the following classes: `is-primary`, `is-success`, `is-danger`. 82 | 83 | If you need any help, you can have a look how to take the content between an opening tag and a closing tag: [Children in JSX](https://reactjs.org/docs/jsx-in-depth.html#children-in-jsx) 84 | 85 | #### Bonus 86 | 87 | If you want, you can do all the cases by using the following object: 88 | 89 | ```javascript 90 | { 91 | isActive: 'is-active', 92 | isBlack: 'is-black', 93 | isCentered: 'is-centered', 94 | isDanger: 'is-danger', 95 | isDark: 'is-dark', 96 | isFocused: 'is-focused', 97 | isGrouped: 'is-grouped', 98 | isHovered: 'is-hovered', 99 | isInfo: 'is-info', 100 | isInverted: 'is-inverted', 101 | isLarge: 'is-large', 102 | isLight: 'is-light', 103 | isLink: 'is-link', 104 | isLoading: 'is-loading', 105 | isMedium: 'is-medium', 106 | isOutlined: 'is-outlined', 107 | isPrimary: 'is-primary', 108 | isRight: 'is-right', 109 | isRounded: 'is-rounded', 110 | isSelected: 'is-selected', 111 | isSmall: 'is-small', 112 | isStatic: 'is-static', 113 | isSuccess: 'is-success', 114 | isText: 'is-text', 115 | isWarning: 'is-warning', 116 | isWhite: 'is-white', 117 | } 118 | ``` 119 | 120 | 121 | ### Iteration 2 | `Navbar` Component 122 | 123 | Create a `Navbar` component, very simple, that displays a link to "Home", "Login" and "Signup" like in the following example: 124 | 125 | ![](https://i.imgur.com/dMaNMeM.png) 126 | 127 | For the "Login" and "Signup" button, you will have to reuse your `Button` React component. 128 | 129 | To help you, you can use the code from the [Bulma Transparent Navbar](https://bulma.io/documentation/components/navbar/#transparent-navbar). 130 | 131 | In the end, you will just need to write `Navbar` in your JSX to display your navbar. 132 | 133 | 134 | ### Iteration 3 | `FormField` Component 135 | 136 | Now it's time to create a new component `FormField` we will use multiple times in the future. 137 | 138 | 139 | 140 | ```html 141 | 142 | 143 | 144 | ``` 145 | ```html 146 | 147 |
    148 | 149 |
    150 | 151 |
    152 |
    153 |
    154 | 155 |
    156 | 157 |
    158 |
    159 | ``` 160 | 161 | What is visually rendered 162 | 163 | ![](https://i.imgur.com/8sKyKxI.png) 164 | 165 | ### Iteration 4 | A Signup Page 166 | 167 | 168 | 169 | Create a Signup page that contains: 170 | - `Navbar` 171 | - A form 172 | - A name `FormField` 173 | - An email `FormField` 174 | - A password `FormField` 175 | - A signup `Button` 176 | 177 | 178 | ### Iteration 5 | Bonus 179 | 180 | Before contunuing, ask for feedback from one of your teachers, he will give you a feedback about what you've done. 181 | 182 | Then, you can: 183 | - Refactor your code 184 | - Create a `Container` component (for the class "container") 185 | - Create a `Message` component, like explained just after 186 | 187 | #### Message Component 188 | 189 | Now, we are going to create `Message` component. You can find the documentation on Bulma's website: https://bulma.io/documentation/components/message/ 190 | 191 | ```html 192 | 193 | 194 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Pellentesque risus mi. 195 | 196 | ``` 197 | 198 | What is visually rendered 199 | 200 | ![](https://i.imgur.com/qmD2Nkb.png) 201 | 202 | -------------------------------------------------------------------------------- /labs/lab-react-data-binding/example.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | React App 7 | 54 | 55 | 56 |
    57 |
    58 |

    IronNutrition

    59 |
    60 | 61 |
    62 |
    63 |
    64 |
    65 |
    66 |
    67 |
    68 | 69 |
    70 |
    71 |
    72 |
    73 |

    74 | Pizza
    75 | 400 cal 76 |

    77 |
    78 |
    79 |
    80 |
    81 |
    82 | 83 |
    84 |
    85 | 86 |
    87 |
    88 |
    89 |
    90 |
    91 |
    92 |
    93 |
    94 |
    95 | 96 |
    97 |
    98 |
    99 |
    100 |

    101 | Salad
    102 | 150 cal 103 |

    104 |
    105 |
    106 |
    107 |
    108 |
    109 | 110 |
    111 |
    112 | 113 |
    114 |
    115 |
    116 |
    117 |
    118 |
    119 |
    120 |
    121 |
    122 | 123 |
    124 |
    125 |
    126 |
    127 |

    128 | Sweet Potato
    129 | 120 cal 130 |

    131 |
    132 |
    133 |
    134 |
    135 |
    136 | 137 |
    138 |
    139 | 140 |
    141 |
    142 |
    143 |
    144 |
    145 |
    146 |
    147 |
    148 |
    149 | 150 |
    151 |
    152 |
    153 |
    154 |

    155 | Gnocchi
    156 | 500 cal 157 |

    158 |
    159 |
    160 |
    161 |
    162 |
    163 | 164 |
    165 |
    166 | 167 |
    168 |
    169 |
    170 |
    171 |
    172 |
    173 |
    174 |

    Today's foods

    175 |
      176 |
    • 1 Pizza = 400 cal
    • 177 |
    • 2 Salad = 300 cal
    • 178 |
    179 | Total: 700 cal 180 |
    181 |
    182 |
    183 |
    184 | 185 | -------------------------------------------------------------------------------- /labs/lab-axios-functional-programming/README.md: -------------------------------------------------------------------------------- 1 | 6 | 7 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 8 | 9 | # Axios and functional programming 10 | 11 | ## Introduction 12 | 13 | The goal of this exercise is to play with the data given by a fake REST API about actors. 14 | 15 | ### The API 16 | 17 | We will play with a fake API that gives infromation about actors. 18 | 19 | This API has only 3 endpoints: 20 | - [`GET /page-1.json`](https://raw.githubusercontent.com/mc100s/training-labs-react/master/src/lab-axios-functional-programming/page-1.json) 21 | - [`GET /page-2.json`](https://raw.githubusercontent.com/mc100s/training-labs-react/master/src/lab-axios-functional-programming/page-2.json) 22 | - [`GET /page-3.json`](https://raw.githubusercontent.com/mc100s/training-labs-react/master/src/lab-axios-functional-programming/page-3.json) 23 | 24 | The base URL of this API is: https://raw.githubusercontent.com/mc100s/training-labs-react/master/src/lab-axios-functional-programming 25 | 26 | 27 | ### The files `api.js` and `dom-manipulation.js` 28 | 29 | `api.js` is the file where you will need to code your calls to the API. 30 | 31 | `dom-maninpulation.js` is the file that use `api.js` to manipulate the API. The only thing you can change in this file is the variable `page` at the top. 32 | 33 | 34 | ### Expectations 35 | 36 | You will find the example of what you should have in the file [`expectations.html`](starter-code/expectations.html) 37 | 38 | ![](https://i.imgur.com/qwwac64.png) 39 | 40 | ## Instructions 41 | 42 | ### Iteration 1 43 | 44 | Let's do this iteration together :) 45 | 46 | The goal is to code the function `getTotalResults` from the file `api.js`. This function is reused in `dom-manipulation.js`. 47 | 48 | First, understand what this function is doing. 49 | 50 | ```js 51 | // From api.js 52 | let service = axios.create({ 53 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 54 | }) 55 | 56 | // From api.js 57 | function getTotalResults(page) { 58 | return service.get(`page-${page}.json`) 59 | .then(response => { 60 | // TODO: Iteration 1 61 | return response.data 62 | }) 63 | } 64 | 65 | // From dom-manipulation.js 66 | let page = 1 67 | 68 | // From dom-manipulation.js 69 | getTotalResults(page) 70 | .then(updateTheDom("getTotalResults")) 71 | 72 | // From dom-manipulation.js 73 | function updateTheDom(elementId) { 74 | return (result) => { 75 | document.getElementById(elementId).innerHTML = JSON.stringify(result, null, 2) 76 | } 77 | } 78 | ``` 79 | 80 | If we replace `updateTheDom` by its return value, this is what we have: 81 | ```js 82 | let service = axios.create({ 83 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 84 | }) 85 | 86 | function getTotalResults(page) { 87 | return service.get(`page-${page}.json`) 88 | .then(response => { 89 | return response.data 90 | }) 91 | } 92 | 93 | let page = 1 94 | 95 | getTotalResults(page) 96 | .then(result => { 97 | document.getElementById("getTotalResults").innerHTML = JSON.stringify(result, null, 2) 98 | } 99 | }) 100 | ``` 101 | 102 | Now, if we replace `getTotalResults` by its return value, this is what we have: 103 | ```js 104 | let service = axios.create({ 105 | baseURL: "https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/" 106 | }) 107 | 108 | let page = 1 109 | 110 | service.get(`page-${page}.json`) 111 | .then(response => { 112 | return response.data 113 | }) 114 | .then(result => { 115 | document.getElementById("getTotalResults").innerHTML = JSON.stringify(result, null, 2) 116 | }) 117 | ``` 118 | 119 | Now it's easier to understand: 120 | 1. We use the API by calling `GET page-1.json` (you can do the same with your browser by going [here](https://raw.githubusercontent.com/mc100s/module-3-react/master/labs/lab-axios-functional-programming/page-1.json)) 121 | 2. We give to the next promise the value `response.data`, that represents the JSON sent by the API 122 | 3. We select the DOM element with the ID `getTotalResults` and we set its content to the value we have. 123 | 124 | That's why if you haven't modified the code yet, you should see the all JSON on the `index.html` page. 125 | 126 | ![](https://i.imgur.com/rYsVbVd.png) 127 | 128 | 129 | Here, the only part that we want to display is the property `total_results` from the JSON. 130 | 131 | That's why we can code the `getTotalResults` like this: 132 | 133 | ```js 134 | function getTotalResults(page) { 135 | return service.get(`page-${page}.json`) 136 | .then(response => { 137 | return response.data.total_results 138 | }) 139 | } 140 | ``` 141 | 142 | ### Iteration 2 143 | 144 | In `api.js`, modify the code of `getFirstResultName` so it gives a promise with the name of the first actor. 145 | 146 | ![](https://i.imgur.com/Cx6OeOc.png) 147 | 148 | 149 | 150 | ### Iteration 3 151 | 152 | In `api.js`, modify the code of `getNames` so it gives a promise with the array of names of the actors. For that we recommand you to use a `map`. 153 | 154 | ![](https://i.imgur.com/ViCh1e3.png) 155 | 156 | 157 | 158 | ### Iteration 4 159 | 160 | In `api.js`, modify the code of `getIdsAndNames` so it gives a promise with the array of "ids and names" like in the following example. 161 | 162 | ![](https://i.imgur.com/jTps66E.png) 163 | 164 | 165 | 166 | ### Iteration 5 167 | 168 | In `api.js`, modify the code of `getSortedNames` so it gives a promise with the array of names sorted by lexicographical order. 169 | 170 | ![](https://i.imgur.com/9cjdJL6.png) 171 | 172 | 173 | ### Iteration 6 174 | 175 | In `api.js`, modify the code of `getNamesFiltered` so it gives a promise with the array of names that contains a `searchTerm`. Be careful, the search should be case insensitive. 176 | 177 | ![](https://i.imgur.com/JmodMh6.png) 178 | 179 | ### Iteration 7 (bonus) 180 | 181 | In `api.js`, modify the code of `getActorNamesWithTheirKnownForMovies` so it gives a promise with the array of "actor names and their know_for movies". 182 | 183 | ![](https://i.imgur.com/ul9TSyq.png) 184 | 185 | 186 | ### Iteration 8 (bonus) 187 | 188 | In `api.js`, create from scratch a function `getKnownForMovies` that will get all the `know_for` movies and find a way to display it in your HTML page. -------------------------------------------------------------------------------- /labs/lab-react-express-recipes/README.md: -------------------------------------------------------------------------------- 1 | ![ck Logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React & Express API | MyRecipeBook 4 | 5 | ## Introduction 6 | 7 | ![](https://camo.githubusercontent.com/38e5628f6fa389ad6f84297ec1bfafb2ee2118b8/68747470733a2f2f73332d65752d776573742d312e616d617a6f6e6177732e636f6d2f69682d6d6174657269616c732f75706c6f6164732f75706c6f61645f61653566646634623732303861386130396532346533306536383234383630662e6a7067) 8 | 9 | Cooking is hard, but you know what's harder? Remembering recipes. Remember that one time you made that amazing dish and forgot what ingredients go into it? It's a terrible feeling. 10 | 11 | In this exercise, we'll make a an application to keep track of your favorite recipes so you never have this disappointing experience again. 12 | 13 | This application can apply to food or dishes, so feel free to alter the language accordingly. 14 | 15 | 16 | ## Instructions 17 | 18 | You are going to make from scratch a web application recipe book with two parts: the front-end in React.js (Client) and the back-end in Express (Server). 19 | 20 | ### Client 21 | 22 | The users that go on your websites will have access to 4 possible routes: 23 | - `/`: Redirect the user to `/dishes` 24 | - `/dishes`: Display a list of all dishes, with a link 25 | - `/dishes/:dishId`: Display the recipe of a specific dish, with the descriptions and all it's ingredients 26 | - `/new-dish`: Display a form page to add a new dish 27 | 28 | **Example of page seen on "/dishes"** 29 | ![/dishes](https://i.imgur.com/qw1ADPz.png) 30 | 31 | 32 | **Example of page seen on "/dishes/598c147d82ff710a38fd6027"** 33 | ![/dishes/:dishId](https://i.imgur.com/SFRNUFe.png) 34 | 35 | 36 | 37 | ### Server 38 | 39 | You are going to create your own Rest API with Express. For that, 2 models are going to be needed: `Ingredient` and `Dish` 40 | 41 | ```javascript 42 | // server/models/ingredient.js 43 | // ... 44 | const IngredientSchema = new Schema({ 45 | name: { 46 | type: String, 47 | required: true 48 | }, 49 | unity: String 50 | }); 51 | module.exports = mongoose.model('Ingredient', IngredientSchema); 52 | ``` 53 | 54 | ```javascript 55 | // server/models/dish.js 56 | // ... 57 | const DishSchema = new Schema({ 58 | name: { 59 | type: String, 60 | required: [true, 'name is required'] 61 | }, 62 | description: { 63 | type: String, 64 | required: [true, 'description is required'] 65 | }, 66 | image: String, 67 | ingredients: [ 68 | { 69 | ingredient: { 70 | type: Schema.Types.ObjectId, 71 | ref: 'Ingredient' 72 | }, 73 | quantity: Number, 74 | _id: false 75 | } 76 | ] 77 | }); 78 | module.exports = mongoose.model('Dish', DishSchema); 79 | ``` 80 | 81 | For your API, you can create the following endpoints: 82 | - `GET /api/dishes`: Returns all dishes with only their id, names and images 83 | - `GET /api/dishes/:dishId`: Returns all information on a specific dish 84 | - `PUT /api/dishes/:dishId`: Updates a specific dish 85 | - `POST /api/dishes`: Creates a new dish 86 | 87 | To help you creating the database, there are 2 files with some data: _data/dishes.json_ and _data/ingredients.json_ 88 | 89 | 90 | ## Iterations 91 | 92 | ### Iteration 1 | Creating your first endpoint 93 | 94 | To start, you will create an endpoint to list all dishes. 95 | 96 | For that, you can: 97 | - Initialize a new Express project inside _starter-code/server/_ folder 98 | - Create your models `Dish` and `Ingredient` 99 | - Populate your database with _data/dishes.json_ and _data/ingredients.json_ 100 | - Create the endpoint `GET /api/dishes`. 101 | 102 | At the end of this iteration, you should see the following result when you go on `localhost:3000/api/dishes`: 103 | 104 | ```json5 105 | [ 106 | { 107 | "_id": "598c147d82ff710a38fd6027", 108 | "name": "Pizza", 109 | "image": "https://i.imgur.com/eTmWoAN.png", 110 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 111 | }, 112 | { 113 | "_id": "598c147d82ff710a38fd6029", 114 | "name": "Sweet Potato", 115 | "image": "https://i.imgur.com/hGraGyR.jpg", 116 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 117 | }, 118 | // ... 119 | ] 120 | ``` 121 | 122 | 123 | ### Iteration 2 | Listing dishes 124 | 125 | Having a link to see all dishes in a JSON is a first good step and now we are going to use a React application. That's why we are now creating a dishes page (`/dishes`) that will display all dishes in a list, with their names and images. 126 | 127 | In that iteration, we recommand you to: 128 | - Initialize a React application in _starter-code/client/_ folder 129 | - Create a component that will be displayed on the following path `/dishes` 130 | - Fetch and display the dishes from `GET /api/dishes` 131 | - Redirect the home page (`/`) to `/dishes` 132 | 133 | 134 | ### Iteration 3 | Show one dish details 135 | 136 | You now have a list of dishes, it's time to link them to a detail page. 137 | 138 | On your list of dishes page, create a link to `/dishes/:dishId` that displays the name, the image and the description of a specific dish. You should also create an "_Edit description_" button just after your description. 139 | 140 | 141 | ### Iteration 4 | Edit the description of a dish 142 | 143 | In this iteration, we are going to edit the description without changing the page! 144 | 145 | In term of user experience, the scenario should be the following: 146 | - The user clicks on the "_Edit description_" button 147 | - The text description is changed by a textarea with the description inside it add the button is changed by "_Save description_" 148 | - When the user clicks on "_Save description_", it replaces the textarea by a simple paragraph, the "_Edit description_" button comes back and the new description must be saved 149 | 150 | For that, you will probably need to create a `PUT /api/dished/:dishId` endpoint. 151 | 152 | 153 | ### Iteration 5 | List all ingredients 154 | 155 | A dish detail page is nice, but we're missing one important piece: *ingredients*. 156 | 157 | Over the next two iterations, we're going to add functionality to add ingredients to our dishes. 158 | 159 | The first step in doing so is going to be displaying a list of possible ingredients on the recipe's page. For that, you will need to: 160 | - Add an API endpoint `GET /api/ingredients` that displays all ingredients 161 | - Use this endpoint to display all your ingredients in your dish detail page (`/dishes/:dishId`) 162 | 163 | In your list of all ingredients, you should display: 164 | - The ingrendient's **name** 165 | - A number input (will be used in the next iteration) 166 | - The **unity** 167 | - A "Add ingredient" button (will be used in the next iteration) 168 | 169 | 170 | ### Iteration 6 | Add ingredient to dish 171 | 172 | Create a function in the single dish component. When someone clicks the "Add ingredient" button from the previous iteration, it should add the ingredient to the dish and display it. 173 | 174 | Do 175 | 176 | The request should be done inside of your dish service. 177 | 178 | The API endpoint is a POST to `'/drinks/:drinkId/ingredients/:id/add'`. It also takes in a parameter for `quantity` in the body, which is a number. 179 | 180 | Add a list of a dish's ingredients to the single dish component. Upon successfully adding the ingredient to the dish, display the ingredient in the list. 181 | 182 | 183 | ### Iteration 7 | Bonus | Creating New Ingredients & Dishes 184 | 185 | Create a new route for adding new dishes. Add a link in the home page to display a form. This form will make a POST request to `/dishes` with a `name`, `image`, and `description`. 186 | 187 | In addition, create a route on the home page to display a form to create a new ingredient. The route is a POST to `/ingredients`, and takes a `name`, `image`, and `description` in the body. 188 | 189 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Module 3 | React & Full-Stack Applications 2 | 3 | 4 | 5 | ## Day 1 | Monday 6 | 7 | [Retrospective](http://learn.ironhack.com/#/learning_unit/2481) 8 | 9 | 10 | [React | Hello React](lessons/1-2-react-hello-react.md) 11 | 12 | 13 | [PP | Axios & Functional Programming](labs/lab-axios-functional-programming/) 14 | 15 | 16 | [React | JSX and elements](lessons/1-3-react-jsx-and-rendering-elements.md) 17 | - [Doc | Hello World](https://reactjs.org/docs/hello-world.html) 18 | - [Doc | Introducing JSX](https://reactjs.org/docs/introducing-jsx.html) 19 | - [Doc | Rendering Elements](https://reactjs.org/docs/rendering-elements.html) 20 | 21 | 22 | [React | Components and Props](lessons/1-4-react-components-and-props.md) 23 | - [Doc | Components and Props](https://reactjs.org/docs/components-and-props.html) 24 | 25 | 26 | [DE | Bulma Components](labs/lab-react-bulma-components/) 27 | 28 | 29 | 30 | 38 | 39 | ## Day 2 | Tuesday 40 | 41 | [React | State and Lifecycle](lessons/2-1-react-state-and-lifecycle.md) 42 | - [Doc | State and Lifecycle](https://reactjs.org/docs/state-and-lifecycle.html) 43 | 44 | 45 | [React | Handling Events & Conditional Rendering](lessons/2-2-react-handling-events-and-conditional-rendering.md) 46 | - [Doc | Handling Events](https://reactjs.org/docs/handling-events.html) 47 | - [Doc | Conditional Rendering](https://reactjs.org/docs/conditional-rendering.html) 48 | 49 | 50 | [React | Lists and Keys](lessons/2-3-react-list-and-keys.md) 51 | - [Doc | Lists and Keys](https://reactjs.org/docs/lists-and-keys.html) 52 | 53 | 54 | [PP | lab-react-ironcontacts](labs/lab-react-ironcontacts/) 55 | 56 | 57 | [React | Forms](lessons/2-4-react-forms.md) 58 | - [Doc | Forms](https://reactjs.org/docs/forms.html) 59 | 60 | 61 | [DE | lab-react-data-binding](labs/lab-react-data-binding/) 62 | 63 | 71 | 72 | ## Day 3 | Wednesday 73 | 74 | 75 | React | Lifting State Up 76 | - [Doc | Lifting State Up](https://reactjs.org/docs/lifting-state-up.html) 77 | 78 | 79 | React | Organize your code 80 | - [Doc | Composition vs Inheritance](https://reactjs.org/docs/composition-vs-inheritance.html) 81 | - [Doc | Thinking In React](https://reactjs.org/docs/thinking-in-react.html) 82 | 83 | 84 | [PP | Reproduce Thinking in React](https://reactjs.org/docs/thinking-in-react.html) 85 | 86 | 87 | React Router | Introduction to Routing 88 | - [A Simple React Router v4 Tutorial](https://medium.com/@pshrmn/a-simple-react-router-v4-tutorial-7f23ff27adf) 89 | - [Doc | Basic Components](https://reacttraining.com/react-router/web/guides/basic-components) 90 | 91 | 92 | React Router | Advanced Routing 93 | - [Doc | `BrowserRouter`](https://reacttraining.com/react-router/web/api/BrowserRouter) 94 | - [Doc | `Route`](https://reacttraining.com/react-router/web/api/Route) 95 | - [Doc | `Switch`](https://reacttraining.com/react-router/web/api/Switch) 96 | - [Doc | `Link`](https://reacttraining.com/react-router/web/api/Link) 97 | - [Doc | `NavLink`](https://reacttraining.com/react-router/web/api/NavLink) 98 | - [Doc | `history`](https://reacttraining.com/react-router/web/api/history) 99 | - [Doc | `location`](https://reacttraining.com/react-router/web/api/location) 100 | - [Doc | `match`](https://reacttraining.com/react-router/web/api/match) 101 | 102 | 103 | [DE | lab-react-wikicountries](labs/lab-react-wiki-countries) 104 | 105 | 113 | 114 | ## Day 4 | Thursday 115 | 116 | React | Calling APIs with Axios 117 | - [Axios documentation](https://github.com/axios/axios) 118 | - [Using Axios with React](https://alligator.io/react/axios-react/) 119 | 120 | 121 | [React | Summary](summary.md) 122 | 123 | 124 | Exercise | Try to reproduce Pokedex.org with pokeapi.co and axios 125 | 126 | 127 | 128 | 136 | 137 | ## Day 5 | Friday 138 | 139 | 140 | [Express | Build a REST API](http://learn.ironhack.com/#/learning_unit/3224) 141 | 142 | 143 | [React & Express | Frontend Backend Integration](lessons/5-2-react-express-integration.md) 144 | 145 | 146 | [DE | MyRecipeBook](labs/lab-react-express-recipes/README.md) 147 | 148 | 149 | 150 | [React & Express | Authentication](https://github.com/ta-web-paris/auth-jwt-lab) 151 | 152 | 153 | [**MERN Boilerplate**](https://github.com/mc100s/mern-boilerplate) 154 | 155 | 156 | [DE | IronForum](https://github.com/ironhack-labs/lab-angular-forum) (in the instructions, replace Angular by React) 157 | 158 | 159 | 167 | 168 | ## Day 6 | Monday 169 | 170 | 171 | 172 | [React & Express | File Upload (Optional)](lessons/6-1-react-express-file-upload.md) 173 | 174 | 175 | [React & Express | Route Guards (Optional)](lessons/6-2-react-express-route-guards.md) 176 | 177 | 178 | React & Express | Deployment 179 | 180 | 187 | 188 | ## Day 7 | Tuesday 189 | 190 | [Project #3: MERN Application](https://docs.google.com/presentation/d/1KToj2CB5XdLjCxnAJd3TIlmozx0PVVdiBlVcsO20Qqo/edit?usp=sharing) 191 | 192 | 193 | ## Extra courses 194 | 195 | [React Bootstrap and SCSS](lessons/extra-react-bootstrap-and-scss.md) 196 | 197 | 198 | [`google-map-react` examples](https://github.com/mc100s/google-map-react-examples) 199 | 200 | 201 | [react-small-exercises](https://github.com/mc100s/react-small-exercises) 202 | 203 | 204 | [ironspace-reactstrap](https://github.com/mc100s/ironspace-reactstrap) 205 | 206 | 207 | 208 | ## Complementary resources 209 | 210 | ### Articles 211 | - [Why Not To Modify React State Directly](https://daveceddia.com/why-not-modify-react-state-directly/) 212 | 213 | ### Code examples 214 | - [React Data Binding (with components A, B, C, D)](https://codesandbox.io/s/p7y3jk8pq0) 215 | - [React IronTodos](https://github.com/mc100s/react-iron-todos) 216 | -------------------------------------------------------------------------------- /labs/lab-react-express-recipes/data/dishes.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Pizza", 4 | "image": "https://i.imgur.com/eTmWoAN.png", 5 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 6 | }, 7 | { 8 | "name": "Salad", 9 | "image": "https://i.imgur.com/DupGBz5.jpg", 10 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 11 | }, 12 | { 13 | "name": "Sweet Potato", 14 | "image": "https://i.imgur.com/hGraGyR.jpg", 15 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 16 | }, 17 | { 18 | "name": "Gnocchi", 19 | "image": "https://i.imgur.com/", 20 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 21 | }, 22 | { 23 | "name": "Pot Roast", 24 | "image": "https://i.imgur.com/WCzJDWz.jpg", 25 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 26 | }, 27 | { 28 | "name": "Lasagna", 29 | "image": "https://i.imgur.com/ClxOafl.jpg", 30 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 31 | }, 32 | { 33 | "name": "Hamburger", 34 | "image": "https://i.imgur.com/LoG39wK.jpg", 35 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 36 | }, 37 | { 38 | "name": "Pad Thai", 39 | "image": "https://i.imgur.com/5ktcSzF.jpg", 40 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 41 | }, 42 | { 43 | "name": "Almonds", 44 | "image": "https://i.imgur.com/JRp4Ksx.jpg", 45 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 46 | }, 47 | { 48 | "name": "Bacon", 49 | "image": "https://i.imgur.com/7GlqDsG.jpg", 50 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 51 | }, 52 | { 53 | "name": "Hot Dog", 54 | "image": "https://i.imgur.com/QqVHdRu.jpg", 55 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 56 | }, 57 | { 58 | "name": "Chocolate Cake", 59 | "image": "https://i.imgur.com/yrgzA9x.jpg", 60 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 61 | }, 62 | { 63 | "name": "Wheat Bread", 64 | "image": "https://i.imgur.com/TsWzMfM.jpg", 65 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 66 | }, 67 | { 68 | "name": "Orange", 69 | "image": "https://i.imgur.com/abKGOcv.jpg", 70 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 71 | }, 72 | { 73 | "name": "Banana", 74 | "image": "https://i.imgur.com/BMdJhu5.jpg", 75 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 76 | }, 77 | { 78 | "name": "Yogurt", 79 | "image": "https://i.imgur.com/URhdrAm.png", 80 | "description": "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum." 81 | } 82 | ] -------------------------------------------------------------------------------- /summary.md: -------------------------------------------------------------------------------- 1 | ![Ironhack logo](https://i.imgur.com/1QgrNNw.png) 2 | 3 | # React Summary 4 | 1. Start a simple React project 5 | 1. JSX 6 | 1. React Components 7 | 1. React Props 8 | 1. React States 9 | 1. React Lifecycle 10 | 1. React Events 11 | 1. React and forms 12 | 1. Conditional rendering 13 | 1. List rendering 14 | 1. React Router 15 | 1. Using Axios with React 16 | 17 | ## Start a simple React project 18 | 19 | ### With Codepen 20 | 21 | - Blank React 14.7 project: https://codepen.io/maxencebouret/pen/MQLWaW/ 22 | - Tic Tac Toe Project: https://codepen.io/gaearon/pen/oWWQNa/ 23 | 24 | ### With your code editor 25 | 26 | Commands to launch 27 | ```sh 28 | $ npm install -g create-react-app # Install globally the `create-react-app` command 29 | $ create-react-app my-app # Create a React project folder "my-app" 30 | $ cd my-app 31 | $ rm -f src/* 32 | $ touch src/index.js src/index.css # Create 2 files 33 | ``` 34 | 35 | Your `src/index.js` file 36 | ```javascript 37 | import React from 'react'; 38 | import ReactDOM from 'react-dom'; 39 | import './index.css'; 40 | 41 | class App extends React.Component { 42 | render() { 43 | return ( 44 |
    45 | {/* Your application code */} 46 |
    47 | ); 48 | } 49 | } 50 | 51 | ReactDOM.render( 52 | , 53 | document.getElementById('root') 54 | ); 55 | ``` 56 | 57 | ## JSX 58 | 59 |
    60 | 61 | 62 | 63 | 64 | 65 | 66 | 71 | 72 | 73 | 74 | 75 | 76 | 77 | 78 | 79 | 80 |
    RuleExample
    A JSX is always wraped by a tag 67 | - <App />
    68 | - <MyCoponent>Hello</MyCoponent>
    69 | - <ul><li>Elt1</li><li>Elt2</li></ul> 70 |
    To put JavaScript, you need to use { and }
    ...
    81 | 82 | 83 | ## React Components 84 | 85 | React is based on Components, and each Component is a classe that `extends React.Component`. 86 | 87 | ```javascript 88 | // Definition of the ShoppingList component 89 | class ShoppingList extends React.Component { 90 | render() { 91 | return ( 92 |
    93 |

    Shopping List for {this.props.owner}

    94 |
      95 |
    • Instagram
    • 96 |
    • WhatsApp
    • 97 |
    • Oculus
    • 98 |
    99 |
    100 | ); 101 | } 102 | } 103 | 104 | class ShoppingList extends React.Component { 105 | render() { 106 | return ( 107 |
    108 | 109 |
    110 | ); 111 | } 112 | } 113 | // Example usage: 114 | ``` 115 | 116 | ### React Functional Components 117 | 118 | Component that only needs a `render` method can be simplified by a function that just returns a JSX. 119 | 120 | **Example** 121 | 122 | ```javascript 123 | function Square(props) { 124 | return ( 125 | 128 | ); 129 | } 130 | ``` 131 | 132 | 133 | ## React Props 134 | 135 | ```javascript 136 | // To use in JSX 137 | 138 | 139 | // To use inside MyComponent class 140 | this.prop.myProp 141 | ``` 142 | 143 | **Example** 144 | ```javascript 145 | class Board extends React.Component { 146 | renderSquare(i) { 147 | return ; // Pass a "value" prop to the Square 148 | } 149 | // ... 150 | } 151 | 152 | class Square extends React.Component { 153 | render() { 154 | return ( 155 | 158 | ); 159 | } 160 | } 161 | ``` 162 | 163 | ## React States 164 | 165 | 166 | ```javascript 167 | // You can initialize the state in the Component's constructor 168 | this.state = { firstname: 'Maxence', age: 25 } 169 | 170 | // You can get a state value with "this.state" property 171 | this.state.firstname 172 | 173 | // You MUST set some state value with "this.setState" method 174 | // Be careful, this opereation is asynchronous 175 | this.setState({firstname: 'Mickaël'}) 176 | ``` 177 | 178 | **Example** 179 | 180 | ```javascript 181 | class Square extends React.Component { 182 | constructor(props) { 183 | super(props); 184 | this.state = { // Init the state 185 | value: null, 186 | }; 187 | } 188 | 189 | render() { 190 | return ( 191 | 194 | ); 195 | } 196 | } 197 | ``` 198 | 199 | ## React Lifecycle 200 | 201 | Mounting: 202 | - [`constructor(props)`](https://reactjs.org/docs/react-component.html#constructor): Should starts with `super(props)`; Perfect to initialize the state and binding methods 203 | - [`componentWillMount()`](https://reactjs.org/docs/react-component.html#componentwillmount) 204 | - [`render()`](https://reactjs.org/docs/react-component.html#render): Return the JSX to display 205 | - [`componentDidMount()`](https://reactjs.org/docs/react-component.html#componentdidmount): Perfect place to call APIs and set up any subscriptions. 206 | 207 | Updating: 208 | - [`componentWillReceiveProps(nextProps)`](https://reactjs.org/docs/react-component.html#componentwillreceiveprops) 209 | - [`shouldComponentUpdate(nextProps, nextState)`](https://reactjs.org/docs/react-component.html#shouldcomponentupdate) 210 | - [`componentWillUpdate(nextProps, nextState)`](https://reactjs.org/docs/react-component.html#componentwillupdate) 211 | - [`render()`](https://reactjs.org/docs/react-component.html#render) 212 | - [`componentDidUpdate(prevProps, prevState)`](https://reactjs.org/docs/react-component.html#componentdidupdate) 213 | 214 | Unmounting: 215 | - [`componentWillUnmount()`](https://reactjs.org/docs/react-component.html#componentwillunmount) 216 | 217 | Error Handling: 218 | - [`componentDidCatch()`](https://reactjs.org/docs/react-component.html#componentdidcatch) 219 | 220 | 221 | ## React Events 222 | 223 | Handling events with React elements is very similar to handling events on DOM elements. There are some syntactic differences: 224 | 225 | - React events are named using camelCase 226 | - With JSX you pass a function as the event handler 227 | 228 | **Examples** 229 | ```javascript 230 | 233 | ``` 234 | 235 | ```javascript 236 | 237 | {/* The same with binding */} 238 | ``` 239 | 240 | ```javascript 241 | class Toggle extends React.Component { 242 | constructor(props) { 243 | super(props); 244 | this.state = {isToggleOn: true}; 245 | 246 | // This binding is necessary to make `this` work in the callback 247 | this.handleClick = this.handleClick.bind(this); 248 | } 249 | 250 | handleClick() { 251 | this.setState(prevState => ({ 252 | isToggleOn: !prevState.isToggleOn 253 | })); 254 | } 255 | 256 | render() { 257 | return ( 258 | 261 | ); 262 | } 263 | } 264 | ``` 265 | 266 | ### List of events 267 | 268 | Event Types | Event Names 269 | -- | -- 270 | Clipboard Events | `onCopy` `onCut` `onPaste` 271 | Composition Events | `onCompositionEnd` `onCompositionStart` `onCompositionUpdate` 272 | Keyboard Events | `onKeyDown` `onKeyPress` `onKeyUp` 273 | Focus Events | `onFocus` `onBlur` 274 | Form Events | `onChange` `onInput` `onInvalid` `onSubmit` 275 | Mouse Events | `onClick` `onContextMenu` `onDoubleClick` `onDrag` `onDragEnd` `onDragEnter` `onDragExit` `onDragLeave` `onDragOver` `onDragStart` `onDrop` `onMouseDown` `onMouseEnter` `onMouseLeave` `onMouseMove` `onMouseOut` `onMouseOver` `onMouseUp` 276 | Selection Events | `onSelect` 277 | Touch Events | `onTouchCancel` `onTouchEnd` `onTouchMove` `onTouchStart` 278 | UI Events | `onScroll` 279 | Wheel Events | `onWheel` 280 | Media Events | `onAbort` `onCanPlay` `onCanPlayThrough` `onDurationChange` `onEmptied` `onEncrypted` `onEnded` `onError` `onLoadedData` `onLoadedMetadata` `onLoadStart` `onPause` `onPlay` `onPlaying` `onProgress` `onRateChange` `onSeeked` `onSeeking` `onStalled` `onSuspend` `onTimeUpdate` `onVolumeChange` `onWaiting` 281 | Image Events | `onLoad` `onError` 282 | Animation Events | `onAnimationStart` `onAnimationEnd` `onAnimationIteration` 283 | Transition Events | `onTransitionEnd` 284 | Other Events | `onToggle` 285 | 286 | ## Conditional rendering 287 | 288 | ```javascript 289 | class MyComponent extends React.Component { 290 | showButton() { 291 | if (this.props.isLoggedIn) 292 | return 293 | else 294 | return 295 | } 296 | render() { 297 | let button 298 | if (this.props.isLoggedIn) 299 | button = 300 | else 301 | button = 302 | return ( 303 |
    304 | {/********** Method 1: Variable **********/} 305 | {button} 306 | {/********** Method 2: Function **********/} 307 | {this.showButton()} 308 | {/********** Method 3: Ternary **********/} 309 | {this.props.isLoggedIn ? : } 310 | {/********** Method 4: Inline If with Logical && Operator **********/} 311 | {this.props.isLoggedIn && } 312 | {!this.props.isLoggedIn && } 313 |
    314 | ) 315 | } 316 | } 317 | ``` 318 | 319 | ## List rendering 320 | 321 | ```javascript 322 | const students = ['Alice', 'Bob', 'Charly', 'David'] 323 | 324 | class MyComponent extends React.Component { 325 | showList() { 326 | let list = [] 327 | for (let i = 0; i < students.length; i++) { 328 | list.push(
  • {students[i]}
  • ) 329 | } 330 | return list 331 | } 332 | render() { 333 | let list = [] 334 | for (let i = 0; i < students.length; i++) { 335 | list.push(
  • {students[i]}
  • ) 336 | } 337 | return ( 338 |
      339 | {/********** Method 1: Variable **********/} 340 | {list} 341 | {/********** Method 2: Function **********/} 342 | {this.showList()} 343 | {/********** Method 3: Map **********/} 344 | {students.map((student,i) =>
    • {student}
    • )} 345 |
    346 | ) 347 | } 348 | } 349 | ``` 350 | 351 | ## React and forms 352 | 353 | **Basic Example with 1 input** 354 | 355 | ```javascript 356 | class NameForm extends React.Component { 357 | constructor(props) { 358 | super(props); 359 | this.state = {value: ''}; 360 | 361 | this.handleChange = this.handleChange.bind(this); 362 | this.handleSubmit = this.handleSubmit.bind(this); 363 | } 364 | 365 | handleChange(event) { 366 | this.setState({value: event.target.value}); 367 | } 368 | 369 | handleSubmit(event) { 370 | alert('A name was submitted: ' + this.state.value); 371 | event.preventDefault(); 372 | } 373 | 374 | render() { 375 | return ( 376 |
    377 | 381 | 382 |
    383 | ); 384 | } 385 | } 386 | ``` 387 | 388 | ## React Router 389 | 390 | ### Installation 391 | ``` 392 | npm install --save react-router-dom 393 | ``` 394 | 395 | ### Import 396 | 397 | ```javascript 398 | import { BrowserRouter, Route, Link } from 'react-router-dom' 399 | ``` 400 | 401 | ### React Router Components 402 | 403 | 404 | 405 | 406 | 407 | 409 | 410 | 411 | 412 | 414 | 415 | 416 | 417 | 418 | 426 | 427 | 428 | 429 | 430 | 432 | 433 | 434 | 435 | 436 | 442 | 443 | 444 | 445 | 446 | 453 | 454 |
    Component Description Main Props 408 |
    <BrowserRouter>Router Component that should wrap your application 413 |
    <Route>When the url matches its props path, it renders its content 419 |
      420 |
    • path: string
    • 421 |
    • component: Component
    • 422 |
    • render: func
    • 423 |
    • exact: bool
    • 424 |
    425 |
    <Switch>Group <Route> together and display maximum 1 431 |
    <Link>Replace the <a> tag of HTML in React Router 437 |
      438 |
    • to: string
    • 439 |
    • to: object
    • 440 |
    441 |
    <NavLink>A special version of the <Link> that will add styling attributes to the rendered element when it matches the current URL 447 |
      448 |
    • activeClassName: string
    • 449 |
    • activeStyle: object
    • 450 |
    • exact: bool
    • 451 |
    452 |
    455 | 456 | 457 | ### `match` 458 | 459 | A component displayed with `` has access to `match` (as `this.props.match` or as `({ match }) => ()`) and it is an object containing the following properties: 460 | 461 | Property | Type | Description 462 | -- | -- | -- 463 | `params`| bool | Key/value pairs parsed from the URL corresponding to the dynamic segments of the path 464 | `isExact`| bool | `true` if the entire URL was matched (no trailing characters) 465 | `path`| string | The path pattern used to match. Useful for building nested ``s 466 | `url`| string | The matched portion of the URL. Useful for building nested ``s 467 | 468 | 469 | ## Using Axios with React 470 | 471 | ### Installation 472 | ``` 473 | npm install --save axios 474 | ``` 475 | 476 | ```javascript 477 | import axios from 'axios' 478 | ``` 479 | 480 | ### Example of GET request 481 | ```javascript 482 | class PersonList extends React.Component { 483 | constructor(props) { 484 | super(props) 485 | this.state = { 486 | persons: [] 487 | } 488 | } 489 | 490 | componentDidMount() { 491 | axios.get(`https://jsonplaceholder.typicode.com/users`) 492 | .then(res => { 493 | const persons = res.data; 494 | this.setState({ persons }); 495 | }) 496 | } 497 | 498 | render() { 499 | return ( 500 |
      501 | { this.state.persons.map(person =>
    • {person.name}
    • ) } 502 |
    503 | ) 504 | } 505 | } 506 | ``` 507 | 508 | ### Example of POST request 509 | 510 | ```javascript 511 | class PersonList extends React.Component { 512 | constructor(props) { 513 | super(props) 514 | this.state = { 515 | name: '' 516 | } 517 | } 518 | 519 | handleChange(event) { 520 | this.setState({ name: event.target.value }); 521 | } 522 | 523 | handleSubmit(event) => { 524 | event.preventDefault(); 525 | const user = { 526 | name: this.state.name 527 | }; 528 | axios.post(`https://jsonplaceholder.typicode.com/users`, { user }) 529 | .then(res => { 530 | console.log(res); 531 | console.log(res.data); 532 | }) 533 | } 534 | 535 | render() { 536 | return ( 537 |
    538 |
    539 | 543 | 544 |
    545 |
    546 | ) 547 | } 548 | } 549 | ``` 550 | 551 | ### Base instance 552 | 553 | ```javascript 554 | // src/api.js 555 | import axios from 'axios'; 556 | 557 | export default axios.create({ 558 | baseURL: `http://jsonplaceholder.typicode.com/` 559 | }); 560 | ``` 561 | 562 | ```javascript 563 | // src/index.js 564 | import API from './api'; 565 | 566 | // ... 567 | API.delete(`users/${this.state.id}`) 568 | .then(res => { 569 | console.log(res); 570 | console.log(res.data); 571 | }) 572 | // ... 573 | ``` 574 | 575 | -------------------------------------------------------------------------------- /labs/lab-react-ironcontacts/starter-code/src/contacts.json: -------------------------------------------------------------------------------- 1 | [ 2 | { 3 | "name": "Idris Elba", 4 | "pictureUrl": "https://image.tmdb.org/t/p/w500/d9NkfCwczP0TjgrjpF94jF67SK8.jpg", 5 | "popularity": 11.622713 6 | }, 7 | { 8 | "name": "Jessica Chastain", 9 | "pictureUrl": "https://image.tmdb.org/t/p/w500/nkFrkn5NZVGWb4b2X0yIcXezhyt.jpg", 10 | "popularity": 8.324357 11 | }, 12 | { 13 | "name": "Johnny Depp", 14 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kbWValANhZI8rbWZXximXuMN4UN.jpg", 15 | "popularity": 15.656534 16 | }, 17 | { 18 | "name": "Emilia Clarke", 19 | "pictureUrl": "https://image.tmdb.org/t/p/w500/j7d083zIMhwnKro3tQqDz2Fq1UD.jpg", 20 | "popularity": 16.211837 21 | }, 22 | { 23 | "name": "Leonardo DiCaprio", 24 | "pictureUrl": "https://image.tmdb.org/t/p/w500/A85WIRIKVsD2DeUSc8wQ4fOKc4e.jpg", 25 | "popularity": 11.245333 26 | }, 27 | { 28 | "name": "Monica Bellucci", 29 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qlT4904d8oi2NIs28RrgnIZDFZB.jpg", 30 | "popularity": 16.096436 31 | }, 32 | { 33 | "name": "Kate Beckinsale", 34 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pTRtcZn9gWQZRiet36qWKh94urn.jpg", 35 | "popularity": 14.669819 36 | }, 37 | { 38 | "name": "Gal Gadot", 39 | "pictureUrl": "https://image.tmdb.org/t/p/w500/34kHAyBaBhq2kUrmhM15paEBuuI.jpg", 40 | "popularity": 10.049256 41 | }, 42 | { 43 | "name": "Ian McKellen", 44 | "pictureUrl": "https://image.tmdb.org/t/p/w500/coWjgMEYJjk2OrNddlXCBm8EIr3.jpg", 45 | "popularity": 10.070132 46 | }, 47 | { 48 | "name": "Benedict Cumberbatch", 49 | "pictureUrl": "https://image.tmdb.org/t/p/w500/2NQH6clGUjJmVSOjWiVD54gurKE.jpg", 50 | "popularity": 9.790722 51 | }, 52 | { 53 | "name": "Naomi Watts", 54 | "pictureUrl": "https://image.tmdb.org/t/p/w500/8W02WOJI1pEGh2iqQsgITR5tV0P.jpg", 55 | "popularity": 10.018392 56 | }, 57 | { 58 | "name": "Mila Kunis", 59 | "pictureUrl": "https://image.tmdb.org/t/p/w500/tc2JwjqC04FckKLuVdRVV2ZdtHn.jpg", 60 | "popularity": 9.727623 61 | }, 62 | { 63 | "name": "Winona Ryder", 64 | "pictureUrl": "https://image.tmdb.org/t/p/w500/gUyEOZpZlGBUkUxCxyoLEc9WejR.jpg", 65 | "popularity": 9.63931 66 | }, 67 | { 68 | "name": "Jodie Foster", 69 | "pictureUrl": "https://image.tmdb.org/t/p/w500/eAIE6bnOQ8rm0f933gyeAQdIwrP.jpg", 70 | "popularity": 9.675204 71 | }, 72 | { 73 | "name": "Sophia Lillis", 74 | "pictureUrl": "https://image.tmdb.org/t/p/w500/r62iGGF7ERQ0oJqq473lcBOVmVr.jpg", 75 | "popularity": 9.551758 76 | }, 77 | { 78 | "name": "Matt Damon", 79 | "pictureUrl": "https://image.tmdb.org/t/p/w500/elSlNgV8xVifsbHpFsqrPGxJToZ.jpg", 80 | "popularity": 9.500475 81 | }, 82 | { 83 | "name": "David Harbour", 84 | "pictureUrl": "https://image.tmdb.org/t/p/w500/chPekukMF5SNnW6b22NbYPqAStr.jpg", 85 | "popularity": 9.47013 86 | }, 87 | { 88 | "name": "Ansel Elgort", 89 | "pictureUrl": "https://image.tmdb.org/t/p/w500/uQYUfGvOZkB5x25Z19UeyLABHmr.jpg", 90 | "popularity": 9.429994 91 | }, 92 | { 93 | "name": "Chris Pratt", 94 | "pictureUrl": "https://image.tmdb.org/t/p/w500/n4DD1AYU7WEMNPLga1TxqnHivn1.jpg", 95 | "popularity": 9.2124 96 | }, 97 | { 98 | "name": "Sylvester Stallone", 99 | "pictureUrl": "https://image.tmdb.org/t/p/w500/gnmwOa46C2TP35N7ARSzboTdx2u.jpg", 100 | "popularity": 9.377661 101 | }, 102 | { 103 | "name": "Maria Bello", 104 | "pictureUrl": "https://image.tmdb.org/t/p/w500/tFkbad0JoWvYc6XYBITv6EfeLwR.jpg", 105 | "popularity": 9.209649 106 | }, 107 | { 108 | "name": "Ryan Reynolds", 109 | "pictureUrl": "https://image.tmdb.org/t/p/w500/h1co81QaT2nJA41Sb7eZwmWl1L2.jpg", 110 | "popularity": 9.457546 111 | }, 112 | { 113 | "name": "Elisabeth Shue", 114 | "pictureUrl": "https://image.tmdb.org/t/p/w500/44AaIXkbZFkdhSW1kRdzCbfYk6c.jpg", 115 | "popularity": 9.335865 116 | }, 117 | { 118 | "name": "Lauren Cohan", 119 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ygzDi7DIY6fHHxAcxvS7Z5kMFHe.jpg", 120 | "popularity": 9.191322 121 | }, 122 | { 123 | "name": "Charlie Sheen", 124 | "pictureUrl": "https://image.tmdb.org/t/p/w500/g4e1QpcNTpmq2uPr5GDNuMvjRuU.jpg", 125 | "popularity": 9.281308 126 | }, 127 | { 128 | "name": "Dwayne Johnson", 129 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kuqFzlYMc2IrsOyPznMd1FroeGq.jpg", 130 | "popularity": 9.236478 131 | }, 132 | { 133 | "name": "Ben Affleck", 134 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cPuPt6mYJ83DjvO3hbjNGug6Fbi.jpg", 135 | "popularity": 9.157077 136 | }, 137 | { 138 | "name": "James McAvoy", 139 | "pictureUrl": "https://image.tmdb.org/t/p/w500/oPIfGm3mf4lbmO5pWwMvfTt5BM1.jpg", 140 | "popularity": 9.098376 141 | }, 142 | { 143 | "name": "Samuel L. Jackson", 144 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dlW6prW9HwYDsIRXNoFYtyHpSny.jpg", 145 | "popularity": 9.076093 146 | }, 147 | { 148 | "name": "Donnie Yen", 149 | "pictureUrl": "https://image.tmdb.org/t/p/w500/vlKBbOc0htUsDGvcxeULcFXDMRo.jpg", 150 | "popularity": 8.90665 151 | }, 152 | { 153 | "name": "Will Smith", 154 | "pictureUrl": "https://image.tmdb.org/t/p/w500/2iYXDlCvLyVO49louRyDDXagZ0G.jpg", 155 | "popularity": 9.038432 156 | }, 157 | { 158 | "name": "Daniel Radcliffe", 159 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kMSMa5tR43TLMR14ahU1neFVytz.jpg", 160 | "popularity": 8.602717 161 | }, 162 | { 163 | "name": "Bryan Cranston", 164 | "pictureUrl": "https://image.tmdb.org/t/p/w500/uwGQELv3FGIGm2KU20tOkcKQ54E.jpg", 165 | "popularity": 8.458914 166 | }, 167 | { 168 | "name": "Scott Adkins", 169 | "pictureUrl": "https://image.tmdb.org/t/p/w500/mX5vlgiyJ8XdvBUMlFe6FVQ9YDh.jpg", 170 | "popularity": 8.801068 171 | }, 172 | { 173 | "name": "Lily James", 174 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rfN0XMbIQLvgGvm4V8ZaTcFa1II.jpg", 175 | "popularity": 8.56171 176 | }, 177 | { 178 | "name": "Bill Skarsgård", 179 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dNBVysW90WipOgX81sAsvxtvddF.jpg", 180 | "popularity": 28.019976 181 | }, 182 | { 183 | "name": "Rosamund Pike", 184 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wevcai4tcGA5niawACntEzgZsKN.jpg", 185 | "popularity": 9.02 186 | }, 187 | { 188 | "name": "Robin Wright", 189 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cke0NNZP4lHRtOethRy2XGSOp3E.jpg", 190 | "popularity": 8.802542 191 | }, 192 | { 193 | "name": "Saori Hara", 194 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cLm0XcEAxTKcEVX3k9gmDLflf7y.jpg", 195 | "popularity": 8.433539 196 | }, 197 | { 198 | "name": "Hugh Jackman", 199 | "pictureUrl": "https://image.tmdb.org/t/p/w500/oOqun0BhA1rLXOi7Q1WdvXAkmW.jpg", 200 | "popularity": 8.58347 201 | }, 202 | { 203 | "name": "Rachel Weisz", 204 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wV2QxhLUHVFAkdvLxzO26o5ncmX.jpg", 205 | "popularity": 8.453556 206 | }, 207 | { 208 | "name": "Liam Neeson", 209 | "pictureUrl": "https://image.tmdb.org/t/p/w500/9mdAohLsDu36WaXV2N3SQ388bvz.jpg", 210 | "popularity": 8.940103 211 | }, 212 | { 213 | "name": "Daniel Craig", 214 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rFuETZeyOAfIqBahOObF7Soq5Dh.jpg", 215 | "popularity": 8.784423 216 | }, 217 | { 218 | "name": "Kate Winslet", 219 | "pictureUrl": "https://image.tmdb.org/t/p/w500/w8wjPbS24vPErNeYhAvtbyAUBMd.jpg", 220 | "popularity": 8.655183 221 | }, 222 | { 223 | "name": "Jonah Hill", 224 | "pictureUrl": "https://image.tmdb.org/t/p/w500/7GXzpyHLCEA36J9biB0wJVAauiO.jpg", 225 | "popularity": 8.501539 226 | }, 227 | { 228 | "name": "Christian Bale", 229 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pPXnqoGD91znz4FwQ6aKuxi6Pcy.jpg", 230 | "popularity": 8.469806 231 | }, 232 | { 233 | "name": "Sam Page", 234 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hCe4MEgugU33IdvDtDkJ6E5siqx.jpg", 235 | "popularity": 8.42623 236 | }, 237 | { 238 | "name": "Robert Pattinson", 239 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wNcm8RiMYlWvneAkqQepkqI6r7L.jpg", 240 | "popularity": 8.428432 241 | }, 242 | { 243 | "name": "Henry Cavill", 244 | "pictureUrl": "https://image.tmdb.org/t/p/w500/h8bn6ybR5Hu58UGJGwb66nrOagV.jpg", 245 | "popularity": 34.132372 246 | }, 247 | { 248 | "name": "Chris Hemsworth", 249 | "pictureUrl": "https://image.tmdb.org/t/p/w500/tlkDiLn2G75Xr7m1ybK8QFzZBso.jpg", 250 | "popularity": 56.23257 251 | }, 252 | { 253 | "name": "Scarlett Johansson", 254 | "pictureUrl": "https://image.tmdb.org/t/p/w500/eYFHUWxTCNg6lPypJCaUQXhoUop.jpg", 255 | "popularity": 19.579901 256 | }, 257 | { 258 | "name": "Jennifer Lawrence", 259 | "pictureUrl": "https://image.tmdb.org/t/p/w500/q0tf3XEo7wa8XglIznTC7WzZ9W3.jpg", 260 | "popularity": 19.560912 261 | }, 262 | { 263 | "name": "Arnold Schwarzenegger", 264 | "pictureUrl": "https://image.tmdb.org/t/p/w500/sOkCXc9xuSr6v7mdAq9LwEBje68.jpg", 265 | "popularity": 18.216362 266 | }, 267 | { 268 | "name": "Jaeden Lieberher", 269 | "pictureUrl": "https://image.tmdb.org/t/p/w500/5iGyfDlrYsQwEeGz8rZibBcxbus.jpg", 270 | "popularity": 19.067514 271 | }, 272 | { 273 | "name": "Natalie Portman", 274 | "pictureUrl": "https://image.tmdb.org/t/p/w500/yBvFSPtUOtuJZWQBkwReTdAD0LU.jpg", 275 | "popularity": 18.074957 276 | }, 277 | { 278 | "name": "Tom Hiddleston", 279 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qB1lHPFBPIzw6I7EvsciZ5wyUNS.jpg", 280 | "popularity": 18.502397 281 | }, 282 | { 283 | "name": "Cate Blanchett", 284 | "pictureUrl": "https://image.tmdb.org/t/p/w500/5HikVWKfkkUa8aLdCMHtREBECIn.jpg", 285 | "popularity": 18.233982 286 | }, 287 | { 288 | "name": "Tom Cruise", 289 | "pictureUrl": "https://image.tmdb.org/t/p/w500/3oWEuo0e8Nx8JvkqYCDec2iMY6K.jpg", 290 | "popularity": 15.789315 291 | }, 292 | { 293 | "name": "Beyoncé Knowles", 294 | "pictureUrl": "https://image.tmdb.org/t/p/w500/xYCtL5BpQFALZGYibVPFsebUs6q.jpg", 295 | "popularity": 16.243559 296 | }, 297 | { 298 | "name": "Charlize Theron", 299 | "pictureUrl": "https://image.tmdb.org/t/p/w500/aHOB9UMgSI7MlXF3GMZaVQXqfQg.jpg", 300 | "popularity": 14.978086 301 | }, 302 | { 303 | "name": "Sean Bean", 304 | "pictureUrl": "https://image.tmdb.org/t/p/w500/iIxP2IzvcLgr5WaTBD4UfSqaV3q.jpg", 305 | "popularity": 14.764385 306 | }, 307 | { 308 | "name": "Jennifer Jason Leigh", 309 | "pictureUrl": "https://image.tmdb.org/t/p/w500/tncFH9YfrK2ql1cNXGEkEAJ5zIz.jpg", 310 | "popularity": 14.264248 311 | }, 312 | { 313 | "name": "Gina Gershon", 314 | "pictureUrl": "https://image.tmdb.org/t/p/w500/jaUQhGVmemkSyS5O0fcRkhGTOUq.jpg", 315 | "popularity": 14.927618 316 | }, 317 | { 318 | "name": "Chloë Grace Moretz", 319 | "pictureUrl": "https://image.tmdb.org/t/p/w500/sNd9meEDU6a0GIp3jFPCeij6bBy.jpg", 320 | "popularity": 11.303077 321 | }, 322 | { 323 | "name": "Ana de Armas", 324 | "pictureUrl": "https://image.tmdb.org/t/p/w500/nwS5UfMT0XUA6JEPwmt0jbNDa7B.jpg", 325 | "popularity": 11.039487 326 | }, 327 | { 328 | "name": "Chris Evans", 329 | "pictureUrl": "https://image.tmdb.org/t/p/w500/8CgFKCZJVwZxa1F88n8drEux0vT.jpg", 330 | "popularity": 10.536705 331 | }, 332 | { 333 | "name": "Sophie Turner", 334 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ed4ajSYdv49j9OF7yMeG8Hznrrt.jpg", 335 | "popularity": 10.673722 336 | }, 337 | { 338 | "name": "Robert Downey Jr.", 339 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1YjdSym1jTG7xjHSI0yGGWEsw5i.jpg", 340 | "popularity": 11.182626 341 | }, 342 | { 343 | "name": "Kevin Spacey", 344 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cdowETe1PgXLjo72hDb7R7tyavf.jpg", 345 | "popularity": 11.075493 346 | }, 347 | { 348 | "name": "James Franco", 349 | "pictureUrl": "https://image.tmdb.org/t/p/w500/nEkRVYr3l3ud8cUZUh9mHMEiUdl.jpg", 350 | "popularity": 11.053895 351 | }, 352 | { 353 | "name": "Elizabeth Olsen", 354 | "pictureUrl": "https://image.tmdb.org/t/p/w500/8vgj3i5ByHUHP6p2jN2o5pcNbaL.jpg", 355 | "popularity": 10.32998 356 | }, 357 | { 358 | "name": "Dustin Hoffman", 359 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ffKPo8ATHVXME6cgA5BDyvy2df1.jpg", 360 | "popularity": 10.438582 361 | }, 362 | { 363 | "name": "Tom Hardy", 364 | "pictureUrl": "https://image.tmdb.org/t/p/w500/4W8v3fX0viPRmwRtS0SfLJW8fkd.jpg", 365 | "popularity": 10.572327 366 | }, 367 | { 368 | "name": "Kristen Stewart", 369 | "pictureUrl": "https://image.tmdb.org/t/p/w500/eiQqvaPMoTjdnlGHyrp2HOBxtMc.jpg", 370 | "popularity": 10.997518 371 | }, 372 | { 373 | "name": "Morgan Freeman", 374 | "pictureUrl": "https://image.tmdb.org/t/p/w500/oGJQhOpT8S1M56tvSsbEBePV5O1.jpg", 375 | "popularity": 10.786083 376 | }, 377 | { 378 | "name": "Mark Ruffalo", 379 | "pictureUrl": "https://image.tmdb.org/t/p/w500/zdM6RgCR5LpZwnL8UA3m7CfVpiq.jpg", 380 | "popularity": 10.378788 381 | }, 382 | { 383 | "name": "Anne Hathaway", 384 | "pictureUrl": "https://image.tmdb.org/t/p/w500/4Nh1zDDrV8ZrhmKCdDfHvZGwOSq.jpg", 385 | "popularity": 10.489662 386 | }, 387 | { 388 | "name": "Steve Carell", 389 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fF9txPQCmHJSTYypJfA3ZzTH9Zr.jpg", 390 | "popularity": 10.227521 391 | }, 392 | { 393 | "name": "Zoe Saldana", 394 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ofNrWiA2KDdqiNxFTLp51HcXUlp.jpg", 395 | "popularity": 10.139613 396 | }, 397 | { 398 | "name": "Shu Qi", 399 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kmTErFq6lKQww2Yk9AfpR2Q5YWx.jpg", 400 | "popularity": 10.445066 401 | }, 402 | { 403 | "name": "Olga Kurylenko", 404 | "pictureUrl": "https://image.tmdb.org/t/p/w500/v9HmONHtTZM4Sl9QSNpxDYvuMCk.jpg", 405 | "popularity": 10.286325 406 | }, 407 | { 408 | "name": "Anthony Hopkins", 409 | "pictureUrl": "https://image.tmdb.org/t/p/w500/jdoBTIru71FbPuHGEgox5RVmIO0.jpg", 410 | "popularity": 10.273801 411 | }, 412 | { 413 | "name": "Anna Raadsveld", 414 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hNeOrwFVEfjUgRsjSUNU6t1gt2N.jpg", 415 | "popularity": 7.926181 416 | }, 417 | { 418 | "name": "Harrison Ford", 419 | "pictureUrl": "https://image.tmdb.org/t/p/w500/7CcoVFTogQgex2kJkXKMe8qHZrC.jpg", 420 | "popularity": 7.926516 421 | }, 422 | { 423 | "name": "John Goodman", 424 | "pictureUrl": "https://image.tmdb.org/t/p/w500/eOIx8zj1vYIRhVY2bK5cjIQfua0.jpg", 425 | "popularity": 7.914248 426 | }, 427 | { 428 | "name": "Leticia Dolera", 429 | "pictureUrl": "https://image.tmdb.org/t/p/w500/6YH2rpENETOqi6iRI66Sbr54hvJ.jpg", 430 | "popularity": 7.881743 431 | }, 432 | { 433 | "name": "Salma Hayek", 434 | "pictureUrl": "https://image.tmdb.org/t/p/w500/u5mg73xKVqm8oT93HoMmsgQHyoK.jpg", 435 | "popularity": 7.880947 436 | }, 437 | { 438 | "name": "Noomi Rapace", 439 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pWYWVk0DsjA19G0AO8006LhATZz.jpg", 440 | "popularity": 7.858482 441 | }, 442 | { 443 | "name": "Angelina Jolie", 444 | "pictureUrl": "https://image.tmdb.org/t/p/w500/nsxtg9MQG01hvud1vVEW9vvfukK.jpg", 445 | "popularity": 7.875641 446 | }, 447 | { 448 | "name": "Kaya Scodelario", 449 | "pictureUrl": "https://image.tmdb.org/t/p/w500/iTrPnn7oS96k0iWPzNxaKCNutB6.jpg", 450 | "popularity": 7.82401 451 | }, 452 | { 453 | "name": "Ellen Page", 454 | "pictureUrl": "https://image.tmdb.org/t/p/w500/paexwBfmlVyzva7q4XgcBdqowmL.jpg", 455 | "popularity": 7.764573 456 | }, 457 | { 458 | "name": "Jack Nicholson", 459 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hINAkm21g80UbaAxA6rHhOaT5Jk.jpg", 460 | "popularity": 7.807676 461 | }, 462 | { 463 | "name": "Cameron Diaz", 464 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ahFkUN9Sm8oF1txUHE5JcJ95Ere.jpg", 465 | "popularity": 7.751413 466 | }, 467 | { 468 | "name": "Kaley Cuoco", 469 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rlt7XJ0dWik2oReZlxOPLI1L7Vt.jpg", 470 | "popularity": 7.734777 471 | }, 472 | { 473 | "name": "Katee Sackhoff", 474 | "pictureUrl": "https://image.tmdb.org/t/p/w500/lVtYSDuIxSaAsd2jW0qKvDTltVi.jpg", 475 | "popularity": 7.725615 476 | }, 477 | { 478 | "name": "Milla Jovovich", 479 | "pictureUrl": "https://image.tmdb.org/t/p/w500/3Tbiz01VIotgRPzPBS77wHVbY97.jpg", 480 | "popularity": 7.74812 481 | }, 482 | { 483 | "name": "Daniel Gillies", 484 | "pictureUrl": "https://image.tmdb.org/t/p/w500/zasTOfb8TNyVGwRfb4jNdHnsZ2m.jpg", 485 | "popularity": 7.859381 486 | }, 487 | { 488 | "name": "Asa Butterfield", 489 | "pictureUrl": "https://image.tmdb.org/t/p/w500/zvaIeivqGmWgCtWusKmItMq3eeC.jpg", 490 | "popularity": 7.847255 491 | }, 492 | { 493 | "name": "Ryan Gosling", 494 | "pictureUrl": "https://image.tmdb.org/t/p/w500/5rOcicCrTCWye0O2S3dnbnWaCr1.jpg", 495 | "popularity": 7.764312 496 | }, 497 | { 498 | "name": "Lauren German", 499 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qhHegRoOL7cZu6rMNRezHbIEj75.jpg", 500 | "popularity": 7.786783 501 | }, 502 | { 503 | "name": "Catherine Zeta-Jones", 504 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fpWTpvzgINGBSIKFCLNR6iQZ0B8.jpg", 505 | "popularity": 7.730205 506 | }, 507 | { 508 | "name": "Elijah Wood", 509 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hHzLAVspGGuPg1KW5JAEsyRvnUT.jpg", 510 | "popularity": 7.7395 511 | }, 512 | { 513 | "name": "Sammo Hung", 514 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hpqwIMmLLpn0mMmpi4EhzARK24K.jpg", 515 | "popularity": 8.367863 516 | }, 517 | { 518 | "name": "Pierce Brosnan", 519 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1JXL0zrA26JjdoX8sqf57fJRDVM.jpg", 520 | "popularity": 8.331166 521 | }, 522 | { 523 | "name": "Michael Fassbender", 524 | "pictureUrl": "https://image.tmdb.org/t/p/w500/oexNPLumoFpazzzUqzBSDDYiUg1.jpg", 525 | "popularity": 8.204842 526 | }, 527 | { 528 | "name": "Elton John", 529 | "pictureUrl": "https://image.tmdb.org/t/p/w500/bFQvidUsrWfWo4ZI7oZgTnB8Zb6.jpg", 530 | "popularity": 8.122038 531 | }, 532 | { 533 | "name": "Takehito Koyasu", 534 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cReYEHxQj6JdLb7bNSUfrGYn44F.jpg", 535 | "popularity": 8.212271 536 | }, 537 | { 538 | "name": "Teodora Duhovnikova", 539 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qgKMeMic4zmZwo4sVG6h8MdMTJt.jpg", 540 | "popularity": 8.158553 541 | }, 542 | { 543 | "name": "Sofia Boutella", 544 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dnFE6lIyN7p0RcS664Xde6mbR7U.jpg", 545 | "popularity": 8.177554 546 | }, 547 | { 548 | "name": "Colin Firth", 549 | "pictureUrl": "https://image.tmdb.org/t/p/w500/lKUq407IhFF6CQoJbUgbEyfS9JA.jpg", 550 | "popularity": 7.994861 551 | }, 552 | { 553 | "name": "Liu Yifei", 554 | "pictureUrl": "https://image.tmdb.org/t/p/w500/bDUD0qMcWP5KvZcVoZR6k5FNLBH.jpg", 555 | "popularity": 8.13985 556 | }, 557 | { 558 | "name": "Brooke D'Orsay", 559 | "pictureUrl": "https://image.tmdb.org/t/p/w500/f3u4a80Y386O16TFLPTAv50q4gX.jpg", 560 | "popularity": 8.047701 561 | }, 562 | { 563 | "name": "Uma Thurman", 564 | "pictureUrl": "https://image.tmdb.org/t/p/w500/6SuOc2R7kXjq3Em24KTNDW9qblJ.jpg", 565 | "popularity": 8.158815 566 | }, 567 | { 568 | "name": "Al Pacino", 569 | "pictureUrl": "https://image.tmdb.org/t/p/w500/z2Ke3YjpBcZoFlNc0auvGYB2ggA.jpg", 570 | "popularity": 8.121115 571 | }, 572 | { 573 | "name": "Michael Keaton", 574 | "pictureUrl": "https://image.tmdb.org/t/p/w500/baeHNv3qrVsnApuKbZXiJOhqMnw.jpg", 575 | "popularity": 8.011631 576 | }, 577 | { 578 | "name": "Andrew Lincoln", 579 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pPJiMODKAz5Z9YTZqYhuHx5cFHF.jpg", 580 | "popularity": 7.933014 581 | }, 582 | { 583 | "name": "Sigourney Weaver", 584 | "pictureUrl": "https://image.tmdb.org/t/p/w500/gxBIAr3CnBjkNRoPovVJCvEGqP0.jpg", 585 | "popularity": 8.103456 586 | }, 587 | { 588 | "name": "Nicole Kidman", 589 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1ammEgq5D6qw5mM4WkgUmnxQ7Uy.jpg", 590 | "popularity": 7.950599 591 | }, 592 | { 593 | "name": "Josh Hutcherson", 594 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fuLYoaiiFhkJzAVj5jOtdZ8FlEl.jpg", 595 | "popularity": 7.926752 596 | }, 597 | { 598 | "name": "Maisie Williams", 599 | "pictureUrl": "https://image.tmdb.org/t/p/w500/6tatYKQDbqz7uGXlIRJGOAvq0ZR.jpg", 600 | "popularity": 7.935621 601 | }, 602 | { 603 | "name": "Lili Reinhart", 604 | "pictureUrl": "https://image.tmdb.org/t/p/w500/iyG660kcBeQImBF4XvieZ9X5GD1.jpg", 605 | "popularity": 7.704611 606 | }, 607 | { 608 | "name": "Millie Bobby Brown", 609 | "pictureUrl": "https://image.tmdb.org/t/p/w500/sLmVJPDBFMXPTXDPMlsLSARhreG.jpg", 610 | "popularity": 7.704422 611 | }, 612 | { 613 | "name": "Jim Carrey", 614 | "pictureUrl": "https://image.tmdb.org/t/p/w500/5tVf0ow8MX4OwjmVoSa5v7qUDka.jpg", 615 | "popularity": 7.632791 616 | }, 617 | { 618 | "name": "Jeremy Renner", 619 | "pictureUrl": "https://image.tmdb.org/t/p/w500/g8gheNEdPSXWH5SnjfjTYWj5ziU.jpg", 620 | "popularity": 7.621639 621 | }, 622 | { 623 | "name": "Shaun Toub", 624 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qRHZD8EdqeUor8A6tazJ3v3gxyD.jpg", 625 | "popularity": 7.599411 626 | }, 627 | { 628 | "name": "Bradley Cooper", 629 | "pictureUrl": "https://image.tmdb.org/t/p/w500/2daC5DeXqwkFND0xxutbnSVKN6c.jpg", 630 | "popularity": 7.596703 631 | }, 632 | { 633 | "name": "Dominic Raacke", 634 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rHMHhPaIjjJHa0IL3KpukJ1vtcV.jpg", 635 | "popularity": 7.596043 636 | }, 637 | { 638 | "name": "Frank Welker", 639 | "pictureUrl": "https://image.tmdb.org/t/p/w500/b3gImArbw13mMKJIe9leFc9YYb7.jpg", 640 | "popularity": 7.518894 641 | }, 642 | { 643 | "name": "Garth Jennings", 644 | "pictureUrl": "https://image.tmdb.org/t/p/w500/zl6ZWijGySSIYJRFalleAiGkxyQ.jpg", 645 | "popularity": 7.450652 646 | }, 647 | { 648 | "name": "Hugo Weaving", 649 | "pictureUrl": "https://image.tmdb.org/t/p/w500/di4A3qhYBIVdlH9DKjqbWfo3FWw.jpg", 650 | "popularity": 7.579291 651 | }, 652 | { 653 | "name": "Elle Fanning", 654 | "pictureUrl": "https://image.tmdb.org/t/p/w500/cbFVl9NWREa0xD2vW9Z3J4ursiu.jpg", 655 | "popularity": 7.474528 656 | }, 657 | { 658 | "name": "Woody Harrelson", 659 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1ecdooAHICUhCZKKEKlFtccEmTU.jpg", 660 | "popularity": 7.394953 661 | }, 662 | { 663 | "name": "Aubrey Plaza", 664 | "pictureUrl": "https://image.tmdb.org/t/p/w500/vlfmCKemFz7WRoolavDZc2cxTAF.jpg", 665 | "popularity": 7.573475 666 | }, 667 | { 668 | "name": "Miki Takakura", 669 | "pictureUrl": "https://image.tmdb.org/t/p/w500/2bXHWy82SzgMCFIuxVRKvDtw8Q6.jpg", 670 | "popularity": 7.563937 671 | }, 672 | { 673 | "name": "Eva Green", 674 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rwmLtchv0uwUYWSNbixY3GGELJ2.jpg", 675 | "popularity": 7.469095 676 | }, 677 | { 678 | "name": "Kevin Costner", 679 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ePo87kGyyY8JZ3z7Zm7Z2GYdmJ8.jpg", 680 | "popularity": 7.393908 681 | }, 682 | { 683 | "name": "Zooey Deschanel", 684 | "pictureUrl": "https://image.tmdb.org/t/p/w500/jJ7FA9i2q6YZg3nJPOyTlWAWcVN.jpg", 685 | "popularity": 7.58965 686 | }, 687 | { 688 | "name": "Jessica Rothe", 689 | "pictureUrl": "https://image.tmdb.org/t/p/w500/8sV1fAj98dwuYZbqvs059QOR18R.jpg", 690 | "popularity": 7.480844 691 | }, 692 | { 693 | "name": "Mark Wahlberg", 694 | "pictureUrl": "https://image.tmdb.org/t/p/w500/z2wJh5n7qZRUE1y9uB8UrivAV2b.jpg", 695 | "popularity": 7.464364 696 | }, 697 | { 698 | "name": "Steven Spielberg", 699 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pOK15UNaw75Bzj7BQO1ulehbPPm.jpg", 700 | "popularity": 7.421246 701 | }, 702 | { 703 | "name": "Mindy Kaling", 704 | "pictureUrl": "https://image.tmdb.org/t/p/w500/Agpd4tJyZ95hk74RifjnfnJpn9U.jpg", 705 | "popularity": 7.320559 706 | }, 707 | { 708 | "name": "Shia LaBeouf", 709 | "pictureUrl": "https://image.tmdb.org/t/p/w500/anP0tygzniIok6L3OxcSZ9TYCF3.jpg", 710 | "popularity": 7.254849 711 | }, 712 | { 713 | "name": "Jake Gyllenhaal", 714 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1dHiMQsqiIAF9zhGvB0oJOIaM16.jpg", 715 | "popularity": 7.177807 716 | }, 717 | { 718 | "name": "Clint Eastwood", 719 | "pictureUrl": "https://image.tmdb.org/t/p/w500/n8h4ZHteFFXfmzUW6OEaPWanDnm.jpg", 720 | "popularity": 7.078098 721 | }, 722 | { 723 | "name": "Kristen Bell", 724 | "pictureUrl": "https://image.tmdb.org/t/p/w500/9DoDVUkoXhT3O2R1RymPlOfUryl.jpg", 725 | "popularity": 7.329863 726 | }, 727 | { 728 | "name": "Brendan Fraser", 729 | "pictureUrl": "https://image.tmdb.org/t/p/w500/n8VOWXp94nhIEo5nS9o6bOpUHiN.jpg", 730 | "popularity": 7.188894 731 | }, 732 | { 733 | "name": "Stanley Tucci", 734 | "pictureUrl": "https://image.tmdb.org/t/p/w500/omGlTJF2IW5r3L3c5y0qkCt3hFr.jpg", 735 | "popularity": 7.166646 736 | }, 737 | { 738 | "name": "Teri Garr", 739 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dG1PeN7MdRlP6ZeSeRmaGwSeUWd.jpg", 740 | "popularity": 7.068717 741 | }, 742 | { 743 | "name": "Kat Dennings", 744 | "pictureUrl": "https://image.tmdb.org/t/p/w500/axPn5eI5mlZtlZJWrvYRq1hCcUh.jpg", 745 | "popularity": 7.288278 746 | }, 747 | { 748 | "name": "Karen Gillan", 749 | "pictureUrl": "https://image.tmdb.org/t/p/w500/h9R0qVLelRt5wBVGbvN9CqNfuGe.jpg", 750 | "popularity": 7.256376 751 | }, 752 | { 753 | "name": "Josh Radnor", 754 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dQkiqkBtS4cOHMIboRxeqJDL8DD.jpg", 755 | "popularity": 7.187438 756 | }, 757 | { 758 | "name": "Annabelle Wallis", 759 | "pictureUrl": "https://image.tmdb.org/t/p/w500/gAO4wcJSBvPfBeO4DB20CjZb3uB.jpg", 760 | "popularity": 7.282546 761 | }, 762 | { 763 | "name": "Glenn Close", 764 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fF6tCfuvuUhaePm5onUNnIE4FvL.jpg", 765 | "popularity": 7.127681 766 | }, 767 | { 768 | "name": "Ai Kayano", 769 | "pictureUrl": "https://image.tmdb.org/t/p/w500/gF7FH1XYb5hIOBiVHF7Bw1e4UF4.jpg", 770 | "popularity": 7.255842 771 | }, 772 | { 773 | "name": "Marion Cotillard", 774 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wuEWDK1D2v26KRDIY7DTHBaSdwA.jpg", 775 | "popularity": 7.060809 776 | }, 777 | { 778 | "name": "Luke Evans", 779 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1d31uDYub8TaLOPWfgX78OotduD.jpg", 780 | "popularity": 7.182368 781 | }, 782 | { 783 | "name": "Rupert Grint", 784 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dFVVJufva2zUSP6WS0pFfR7g8uN.jpg", 785 | "popularity": 7.193027 786 | }, 787 | { 788 | "name": "Tessa Thompson", 789 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fycqdiiM6dsNSbnONBVVQ57ILV1.jpg", 790 | "popularity": 7.090946 791 | }, 792 | { 793 | "name": "Vince McMahon", 794 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kslrqPb5KCjODE6oZmU3XUo2U81.jpg", 795 | "popularity": 7.175863 796 | }, 797 | { 798 | "name": "Linda Hamilton", 799 | "pictureUrl": "https://image.tmdb.org/t/p/w500/fcRpgjonpH3WmPs0V63g7iP7Dbm.jpg", 800 | "popularity": 7.073388 801 | }, 802 | { 803 | "name": "Lee Chae-dam", 804 | "pictureUrl": "https://image.tmdb.org/t/p/w500/lJ2ryu3XGVyP7WESkL3pTigeg8j.jpg", 805 | "popularity": 14.042809 806 | }, 807 | { 808 | "name": "Emma Watson", 809 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pMjCFPe3oLBaVXw7qfFzrwA0WXD.jpg", 810 | "popularity": 14.257674 811 | }, 812 | { 813 | "name": "Tom Hanks", 814 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pQFoyx7rp09CJTAb932F2g8Nlho.jpg", 815 | "popularity": 13.978997 816 | }, 817 | { 818 | "name": "Finn Wolfhard", 819 | "pictureUrl": "https://image.tmdb.org/t/p/w500/xovGi4x7OXG8ZUfljIoWLexV7fM.jpg", 820 | "popularity": 13.367961 821 | }, 822 | { 823 | "name": "Carla Gugino", 824 | "pictureUrl": "https://image.tmdb.org/t/p/w500/xddYLCp8zWLgYcQRck7REEgCUWl.jpg", 825 | "popularity": 13.903457 826 | }, 827 | { 828 | "name": "Matt Bomer", 829 | "pictureUrl": "https://image.tmdb.org/t/p/w500/5MeRAylq6FeVMyay3sjHNTKnljL.jpg", 830 | "popularity": 13.566747 831 | }, 832 | { 833 | "name": "Bruce Willis", 834 | "pictureUrl": "https://image.tmdb.org/t/p/w500/kI1OluWhLJk3pnR19VjOfABpnTY.jpg", 835 | "popularity": 13.337271 836 | }, 837 | { 838 | "name": "Michael Caine", 839 | "pictureUrl": "https://image.tmdb.org/t/p/w500/vvj0JMSFpOajXCE46Hy4dyqSP2U.jpg", 840 | "popularity": 13.34082 841 | }, 842 | { 843 | "name": "Cara Delevingne", 844 | "pictureUrl": "https://image.tmdb.org/t/p/w500/lEIFRIR8EohwOVe7PQu4zvIl850.jpg", 845 | "popularity": 13.272943 846 | }, 847 | { 848 | "name": "Alexandra Daddario", 849 | "pictureUrl": "https://image.tmdb.org/t/p/w500/z636bIgZDE2HP6R3z9ij87F1sme.jpg", 850 | "popularity": 12.648279 851 | }, 852 | { 853 | "name": "Jason Statham", 854 | "pictureUrl": "https://image.tmdb.org/t/p/w500/PhWiWgasncGWD9LdbsGcmxkV4r.jpg", 855 | "popularity": 12.503937 856 | }, 857 | { 858 | "name": "Rose Byrne", 859 | "pictureUrl": "https://image.tmdb.org/t/p/w500/laJdQNmsuR2iblYUggEqr49LvwJ.jpg", 860 | "popularity": 13.094365 861 | }, 862 | { 863 | "name": "Brad Pitt", 864 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ejYIW1enUcGJ9GS3Bs34mtONwWS.jpg", 865 | "popularity": 12.626348 866 | }, 867 | { 868 | "name": "Lena Headey", 869 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wcpy6J7KLzmVt0METboX3CZ0Jp.jpg", 870 | "popularity": 11.916478 871 | }, 872 | { 873 | "name": "Sandra Bullock", 874 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1Im0NV6nVaKyXMpYjGOfWaj5khS.jpg", 875 | "popularity": 12.291694 876 | }, 877 | { 878 | "name": "Keanu Reeves", 879 | "pictureUrl": "https://image.tmdb.org/t/p/w500/1wpzvf5PaQ1AZjl5rPNjWQobLLP.jpg", 880 | "popularity": 12.267253 881 | }, 882 | { 883 | "name": "Jackie Chan", 884 | "pictureUrl": "https://image.tmdb.org/t/p/w500/pmKJ4sGvPQ3imzXaFnjW4Vk5Gyc.jpg", 885 | "popularity": 11.628039 886 | }, 887 | { 888 | "name": "Vin Diesel", 889 | "pictureUrl": "https://image.tmdb.org/t/p/w500/3RdYMTLoL1X16djGF52cFtJovDT.jpg", 890 | "popularity": 11.876613 891 | }, 892 | { 893 | "name": "Andy Serkis", 894 | "pictureUrl": "https://image.tmdb.org/t/p/w500/nQRsxFveJaUIlZ4GYWDe9uJ6u2f.jpg", 895 | "popularity": 11.350167 896 | }, 897 | { 898 | "name": "Shohreh Aghdashloo", 899 | "pictureUrl": "https://image.tmdb.org/t/p/w500/iSx8zmrDe4jd7xXZvLpfJ2d3rmM.jpg", 900 | "popularity": 7.047174 901 | }, 902 | { 903 | "name": "Julia Roberts", 904 | "pictureUrl": "https://image.tmdb.org/t/p/w500/yzaIyUEKHSnEYDwltXs8gpF4SVC.jpg", 905 | "popularity": 7.055251 906 | }, 907 | { 908 | "name": "Keira Knightley", 909 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rv6quYbTgFTmBAoePwy5xuurW3g.jpg", 910 | "popularity": 7.031087 911 | }, 912 | { 913 | "name": "Megan Fox", 914 | "pictureUrl": "https://image.tmdb.org/t/p/w500/yOnFnJmVw4RuGjcTNREFBso7pEp.jpg", 915 | "popularity": 7.01965 916 | }, 917 | { 918 | "name": "Alicia Vikander", 919 | "pictureUrl": "https://image.tmdb.org/t/p/w500/q5PMBDNAFdAgeFsLDI2pQ2Fr6a6.jpg", 920 | "popularity": 7.02548 921 | }, 922 | { 923 | "name": "Kim Yoo-Yeon", 924 | "pictureUrl": "https://image.tmdb.org/t/p/w500/v6Kz62B8Tvfe7HQcjWKvFGUM7qZ.jpg", 925 | "popularity": 7.011597 926 | }, 927 | { 928 | "name": "John Travolta", 929 | "pictureUrl": "https://image.tmdb.org/t/p/w500/ns8uZHEHzV18ifqA9secv8c2Ard.jpg", 930 | "popularity": 6.98798 931 | }, 932 | { 933 | "name": "Ron Howard", 934 | "pictureUrl": "https://image.tmdb.org/t/p/w500/qdtdnNLSsaXZmpwOqXuQB3xU2uL.jpg", 935 | "popularity": 6.984089 936 | }, 937 | { 938 | "name": "Grainger Hines", 939 | "pictureUrl": "https://image.tmdb.org/t/p/w500/8eKbbE6CCtPS5NcIRQ7evoORr22.jpg", 940 | "popularity": 7.001197 941 | }, 942 | { 943 | "name": "Robert Rodriguez", 944 | "pictureUrl": "https://image.tmdb.org/t/p/w500/hD7VEBUSroabACzfuZk43pPvNXO.jpg", 945 | "popularity": 6.974719 946 | }, 947 | { 948 | "name": "Norman Reedus", 949 | "pictureUrl": "https://image.tmdb.org/t/p/w500/wJBL3VdMdMD5OarXEVHmSoupiLT.jpg", 950 | "popularity": 6.988148 951 | }, 952 | { 953 | "name": "Stan Lee", 954 | "pictureUrl": "https://image.tmdb.org/t/p/w500/dTr2gJPL7jELKVkcjtoNx80uVKR.jpg", 955 | "popularity": 6.948084 956 | }, 957 | { 958 | "name": "Thomas Middleditch", 959 | "pictureUrl": "https://image.tmdb.org/t/p/w500/eejkzd9gtYm7pMFiVdjsGBYde5H.jpg", 960 | "popularity": 6.945808 961 | }, 962 | { 963 | "name": "Willem Dafoe", 964 | "pictureUrl": "https://image.tmdb.org/t/p/w500/xM5lhOR5tWWdIlFpBDeZJx9opIP.jpg", 965 | "popularity": 6.958265 966 | }, 967 | { 968 | "name": "Orlando Bloom", 969 | "pictureUrl": "https://image.tmdb.org/t/p/w500/6JmscW0HXS10420mzdgjKXfWdkX.jpg", 970 | "popularity": 6.955899 971 | }, 972 | { 973 | "name": "Kristen Wiig", 974 | "pictureUrl": "https://image.tmdb.org/t/p/w500/lh42BSDh30GD65nfCTWDorZE46Z.jpg", 975 | "popularity": 6.939361 976 | }, 977 | { 978 | "name": "Leonard Nimoy", 979 | "pictureUrl": "https://image.tmdb.org/t/p/w500/rTQulQ5WMehA3SSBnGTmayTsF0m.jpg", 980 | "popularity": 6.842567 981 | }, 982 | { 983 | "name": "Claire Forlani", 984 | "pictureUrl": "https://image.tmdb.org/t/p/w500/6XIXq8n2epBQBvnbU1BXyNJyPYA.jpg", 985 | "popularity": 6.84498 986 | }, 987 | { 988 | "name": "Rachel McAdams", 989 | "pictureUrl": "https://image.tmdb.org/t/p/w500/c60WxtQceDxOp7sd2iWhOqn5Y2l.jpg", 990 | "popularity": 6.923095 991 | }, 992 | { 993 | "name": "Jean-Claude Van Damme", 994 | "pictureUrl": "https://image.tmdb.org/t/p/w500/aqZ9RjL5j44HMlBMvTaawhHiGOH.jpg", 995 | "popularity": 6.914606 996 | } 997 | ] --------------------------------------------------------------------------------