├── .gitignore ├── 02_create_a_react_app ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.css │ ├── App.jsx │ ├── index.css │ └── main.jsx └── vite.config.js ├── 03_creating_a_react_component ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.jsx │ ├── Person.jsx │ └── main.jsx └── vite.config.js ├── 04_JSX ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.jsx │ ├── Person.jsx │ └── main.jsx └── vite.config.js ├── 05_events ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.css │ ├── People.jsx │ ├── Person.jsx │ └── main.jsx └── vite.config.js ├── 06_styling ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.css │ ├── People.jsx │ ├── Person.jsx │ ├── main.css │ └── main.jsx └── vite.config.js ├── 07_state ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.css │ ├── People.jsx │ ├── Person.jsx │ ├── main.css │ └── main.jsx └── vite.config.js ├── 08_props ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.css │ ├── People.jsx │ ├── Person.jsx │ ├── main.css │ └── main.jsx └── vite.config.js ├── 09_composition ├── .eslintrc.cjs ├── .gitignore ├── README.md ├── index.html ├── package.json ├── src │ ├── App.jsx │ ├── People.css │ ├── People.jsx │ ├── PeopleQuery.jsx │ ├── Person.css │ ├── Person.jsx │ ├── main.css │ └── main.jsx └── vite.config.js ├── LICENSE ├── README.md ├── ReactCheatSheet.md └── assets ├── Person.css ├── Person.html ├── README.md └── fetchPeople.js /.gitignore: -------------------------------------------------------------------------------- 1 | **/sensitiveConstants.js 2 | **/package-lock.json 3 | **/yarn.lock 4 | 5 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 6 | 7 | # dependencies 8 | **/node_modules 9 | **/.pnp 10 | **/.pnp.js 11 | 12 | # testing 13 | **/coverage 14 | 15 | # production 16 | **/build 17 | 18 | # misc 19 | **/.DS_Store 20 | **/.env.local 21 | **/.env.development.local 22 | **/.env.test.local 23 | **/.env.production.local 24 | 25 | **/npm-debug.log* 26 | **/yarn-debug.log* 27 | **/yarn-error.log* 28 | -------------------------------------------------------------------------------- /02_create_a_react_app/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /02_create_a_react_app/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /02_create_a_react_app/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/02_create_a_react_app/README.md -------------------------------------------------------------------------------- /02_create_a_react_app/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /02_create_a_react_app/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.3.1", 14 | "react-dom": "^18.3.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.3.3", 18 | "@types/react-dom": "^18.3.0", 19 | "@vitejs/plugin-react-swc": "^3.3.2", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /02_create_a_react_app/src/App.css: -------------------------------------------------------------------------------- 1 | #root { 2 | max-width: 1280px; 3 | margin: 0 auto; 4 | padding: 2rem; 5 | text-align: center; 6 | } 7 | 8 | .logo { 9 | height: 6em; 10 | padding: 1.5em; 11 | will-change: filter; 12 | transition: filter 300ms; 13 | } 14 | .logo:hover { 15 | filter: drop-shadow(0 0 2em #646cffaa); 16 | } 17 | .logo.react:hover { 18 | filter: drop-shadow(0 0 2em #61dafbaa); 19 | } 20 | 21 | @keyframes logo-spin { 22 | from { 23 | transform: rotate(0deg); 24 | } 25 | to { 26 | transform: rotate(360deg); 27 | } 28 | } 29 | 30 | @media (prefers-reduced-motion: no-preference) { 31 | a:nth-of-type(2) .logo { 32 | animation: logo-spin infinite 20s linear; 33 | } 34 | } 35 | 36 | .card { 37 | padding: 2em; 38 | } 39 | 40 | .read-the-docs { 41 | color: #888; 42 | } 43 | -------------------------------------------------------------------------------- /02_create_a_react_app/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import './App.css' 3 | 4 | function App() { 5 | const [count, setCount] = useState(0) 6 | 7 | return ( 8 | <> 9 |

Vite + React

10 |
11 | 14 |

15 | Edit src/App.jsx and save to test HMR 16 |

17 |
18 |

19 | Click on the Vite and React logos to learn more 20 |

21 | 22 | ) 23 | } 24 | 25 | export default App 26 | -------------------------------------------------------------------------------- /02_create_a_react_app/src/index.css: -------------------------------------------------------------------------------- 1 | :root { 2 | font-family: Inter, system-ui, Avenir, Helvetica, Arial, sans-serif; 3 | line-height: 1.5; 4 | font-weight: 400; 5 | 6 | color-scheme: light dark; 7 | color: rgba(255, 255, 255, 0.87); 8 | background-color: #242424; 9 | 10 | font-synthesis: none; 11 | text-rendering: optimizeLegibility; 12 | -webkit-font-smoothing: antialiased; 13 | -moz-osx-font-smoothing: grayscale; 14 | -webkit-text-size-adjust: 100%; 15 | } 16 | 17 | a { 18 | font-weight: 500; 19 | color: #646cff; 20 | text-decoration: inherit; 21 | } 22 | a:hover { 23 | color: #535bf2; 24 | } 25 | 26 | body { 27 | margin: 0; 28 | display: flex; 29 | place-items: center; 30 | min-width: 320px; 31 | min-height: 100vh; 32 | } 33 | 34 | h1 { 35 | font-size: 3.2em; 36 | line-height: 1.1; 37 | } 38 | 39 | button { 40 | border-radius: 8px; 41 | border: 1px solid transparent; 42 | padding: 0.6em 1.2em; 43 | font-size: 1em; 44 | font-weight: 500; 45 | font-family: inherit; 46 | background-color: #1a1a1a; 47 | cursor: pointer; 48 | transition: border-color 0.25s; 49 | } 50 | button:hover { 51 | border-color: #646cff; 52 | } 53 | button:focus, 54 | button:focus-visible { 55 | outline: 4px auto -webkit-focus-ring-color; 56 | } 57 | 58 | @media (prefers-color-scheme: light) { 59 | :root { 60 | color: #213547; 61 | background-color: #ffffff; 62 | } 63 | a:hover { 64 | color: #747bff; 65 | } 66 | button { 67 | background-color: #f9f9f9; 68 | } 69 | } 70 | -------------------------------------------------------------------------------- /02_create_a_react_app/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './index.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | 9 | , 10 | ) 11 | -------------------------------------------------------------------------------- /02_create_a_react_app/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /03_creating_a_react_component/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /03_creating_a_react_component/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /03_creating_a_react_component/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/03_creating_a_react_component/README.md -------------------------------------------------------------------------------- /03_creating_a_react_component/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /03_creating_a_react_component/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.3.1", 14 | "react-dom": "^18.3.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.3.3", 18 | "@types/react-dom": "^18.3.0", 19 | "@vitejs/plugin-react-swc": "^3.3.2", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /03_creating_a_react_component/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { People } from "./People" 2 | 3 | function App() { 4 | return ( 5 | <> 6 |
7 |
8 |
9 | 10 |
11 | 14 | 15 | ) 16 | } 17 | 18 | export default App 19 | -------------------------------------------------------------------------------- /03_creating_a_react_component/src/People.jsx: -------------------------------------------------------------------------------- 1 | import { Person } from "./Person"; 2 | 3 | export function People() { 4 | return ( 5 | <> 6 |

People

7 | 8 |

No people to show

9 | 10 | 11 | ) 12 | } -------------------------------------------------------------------------------- /03_creating_a_react_component/src/Person.jsx: -------------------------------------------------------------------------------- 1 | export const Person = () => { 2 | return ( 3 | <> 4 |

Person

5 | 6 | ); 7 | } -------------------------------------------------------------------------------- /03_creating_a_react_component/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | 5 | ReactDOM.createRoot(document.getElementById('root')).render( 6 | 7 | 8 | , 9 | ) 10 | -------------------------------------------------------------------------------- /03_creating_a_react_component/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /04_JSX/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /04_JSX/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /04_JSX/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/04_JSX/README.md -------------------------------------------------------------------------------- /04_JSX/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /04_JSX/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.3.1", 14 | "react-dom": "^18.3.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.3.3", 18 | "@types/react-dom": "^18.3.0", 19 | "@vitejs/plugin-react-swc": "^3.3.2", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /04_JSX/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { People } from "./People" 2 | 3 | function App() { 4 | const people = [ 5 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 6 | { id: 1, name: { first: "Monica", last: "Geller" } }, 7 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 8 | { id: 3, name: { first: "Rachel", last: "Green" } }, 9 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 10 | ]; 11 | return ( 12 | <> 13 |
14 |
15 |
16 | 17 | {people ? :

No people to show

} 18 |
19 | 22 | 23 | ) 24 | } 25 | 26 | export default App 27 | -------------------------------------------------------------------------------- /04_JSX/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | 4 | export function People({ people }) { 5 | return ( 6 | <> 7 |

People

8 | {people.map(person =>
{person.name.first} {person.name.last}
)} 9 | 10 | 11 | ) 12 | } -------------------------------------------------------------------------------- /04_JSX/src/Person.jsx: -------------------------------------------------------------------------------- 1 | export const Person = () => { 2 | return ( 3 | <> 4 |

Person

5 | 6 | ); 7 | } -------------------------------------------------------------------------------- /04_JSX/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | 5 | ReactDOM.createRoot(document.getElementById('root')).render( 6 | 7 | 8 | , 9 | ) 10 | -------------------------------------------------------------------------------- /04_JSX/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /05_events/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /05_events/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /05_events/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/05_events/README.md -------------------------------------------------------------------------------- /05_events/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /05_events/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "react": "^18.3.1", 14 | "react-dom": "^18.3.1" 15 | }, 16 | "devDependencies": { 17 | "@types/react": "^18.3.3", 18 | "@types/react-dom": "^18.3.0", 19 | "@vitejs/plugin-react-swc": "^3.3.2", 20 | "eslint": "^8.45.0", 21 | "eslint-plugin-react": "^7.32.2", 22 | "eslint-plugin-react-hooks": "^4.6.0", 23 | "eslint-plugin-react-refresh": "^0.4.3", 24 | "vite": "^4.4.5" 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /05_events/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { People } from "./People" 2 | 3 | function App() { 4 | const people = [ 5 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 6 | { id: 1, name: { first: "Monica", last: "Geller" } }, 7 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 8 | { id: 3, name: { first: "Rachel", last: "Green" } }, 9 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 10 | ]; 11 | const numberToFetch = 10; 12 | const gender = "all"; 13 | return ( 14 | <> 15 |
16 |
17 |
18 |
19 |
20 | 21 | console.log(e.target.value)} type="number" id="numberToFetch" value={numberToFetch} /> 22 |
23 |
24 | 25 | 30 |
31 |
32 | 33 | {people ? :

No people to show

} 34 |
35 |
36 | Copyright © Us.com {(new Date()).getFullYear()} 37 |
38 | 39 | ) 40 | 41 | function fetchPeople(numberToFetch, gender) { 42 | const url = `https://randomuser.me/api/?results=${numberToFetch}&gender=${gender}&seed=42` 43 | fetch(url) 44 | .then(res => res.json()) 45 | .then(res => res.results) 46 | .then(people => console.log("People", people)) 47 | .catch(err => console.error("Error fetching", err)) 48 | } 49 | } 50 | 51 | export default App 52 | -------------------------------------------------------------------------------- /05_events/src/People.css: -------------------------------------------------------------------------------- 1 | .People {} -------------------------------------------------------------------------------- /05_events/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | import './People.css' 4 | 5 | export function People({ people }) { 6 | return ( 7 |
8 |

People

9 |
10 | {people.map(person =>
{person.name.first} {person.name.last}
)} 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | const styles = { 18 | wrapper: { 19 | display: "grid", 20 | gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", 21 | } 22 | } -------------------------------------------------------------------------------- /05_events/src/Person.jsx: -------------------------------------------------------------------------------- 1 | export const Person = () => { 2 | return ( 3 | <> 4 |

Person

5 | 6 | ); 7 | } -------------------------------------------------------------------------------- /05_events/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | 5 | ReactDOM.createRoot(document.getElementById('root')).render( 6 | 7 | 8 | , 9 | ) 10 | -------------------------------------------------------------------------------- /05_events/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /06_styling/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /06_styling/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /06_styling/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/06_styling/README.md -------------------------------------------------------------------------------- /06_styling/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /06_styling/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.3.3", 19 | "@types/react-dom": "^18.3.0", 20 | "@vitejs/plugin-react-swc": "^3.3.2", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /06_styling/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { People } from "./People" 2 | 3 | function App() { 4 | const people = [ 5 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 6 | { id: 1, name: { first: "Monica", last: "Geller" } }, 7 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 8 | { id: 3, name: { first: "Rachel", last: "Green" } }, 9 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 10 | ]; 11 | const numberToFetch = 100; 12 | const gender = "all"; 13 | return ( 14 | <> 15 |
16 |
17 |
18 |
19 |
20 | 21 | console.log(e.target.value)} type="number" id="numberToFetch" value={numberToFetch} /> 22 |
23 |
24 | 25 | 30 |
31 |
32 | 33 | {people ? :

No people to show

} 34 |
35 |
36 | Copyright © Us.com {(new Date()).getFullYear()} 37 |
38 | 39 | ) 40 | 41 | function fetchPeople(numberToFetch, gender) { 42 | const url = `https://randomuser.me/api/?results=${numberToFetch}&gender=${gender}` 43 | fetch(url) 44 | .then(res => res.json()) 45 | .then(res => res.results) 46 | .then(people => console.log("People", people)) 47 | .catch(err => console.error("Error fetching", err)) 48 | } 49 | 50 | } 51 | 52 | export default App 53 | -------------------------------------------------------------------------------- /06_styling/src/People.css: -------------------------------------------------------------------------------- 1 | .People {} -------------------------------------------------------------------------------- /06_styling/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | import './People.css' 4 | 5 | export function People({ people }) { 6 | return ( 7 |
8 |

People

9 |
10 | {people.map(person =>
{person.name.first} {person.name.last}
)} 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | const styles = { 18 | wrapper: { 19 | display: "grid", 20 | gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", 21 | } 22 | } -------------------------------------------------------------------------------- /06_styling/src/Person.jsx: -------------------------------------------------------------------------------- 1 | export const Person = () => { 2 | return ( 3 | <> 4 |

Person

5 | 6 | ); 7 | } -------------------------------------------------------------------------------- /06_styling/src/main.css: -------------------------------------------------------------------------------- 1 | :root {} -------------------------------------------------------------------------------- /06_styling/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './main.css' 5 | import 'bootstrap/dist/css/bootstrap.css' 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | , 11 | ) 12 | -------------------------------------------------------------------------------- /06_styling/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /07_state/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /07_state/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /07_state/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/07_state/README.md -------------------------------------------------------------------------------- /07_state/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /07_state/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.3.3", 19 | "@types/react-dom": "^18.3.0", 20 | "@vitejs/plugin-react-swc": "^3.3.2", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /07_state/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { People } from "./People" 3 | 4 | function App() { 5 | const [people, setPeople] = useState([ 6 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 7 | { id: 1, name: { first: "Monica", last: "Geller" } }, 8 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 9 | { id: 3, name: { first: "Rachel", last: "Green" } }, 10 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 11 | ]); 12 | const [numberToFetch, setNumberToFetch] = useState(10); 13 | const [gender, setGender] = useState("all"); 14 | return ( 15 | <> 16 |
17 |
18 |
19 |
20 |
21 | 22 | setNumberToFetch(e.target.value)} type="number" id="numberToFetch" value={numberToFetch} /> 23 |
24 |
25 | 26 | 31 |
32 |
33 | 34 | {people ? :

No people to show

} 35 |
36 |
37 | Copyright © Us.com {(new Date()).getFullYear()} 38 |
39 | 40 | ) 41 | 42 | function fetchPeople(numberToFetch, gender) { 43 | const url = `https://randomuser.me/api/?results=${numberToFetch}&gender=${gender}` 44 | fetch(url) 45 | .then(res => res.json()) 46 | .then(res => res.results) 47 | .then(people => people.map((p, i) => ({ ...p, id: i }))) 48 | .then(people => setPeople(people)) 49 | .catch(err => console.error("Error fetching", err)) 50 | } 51 | } 52 | 53 | export default App 54 | -------------------------------------------------------------------------------- /07_state/src/People.css: -------------------------------------------------------------------------------- 1 | .People {} -------------------------------------------------------------------------------- /07_state/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | import './People.css' 4 | 5 | export function People({ people }) { 6 | return ( 7 |
8 |

People

9 |
10 | {people.map(person =>
{person.name.first} {person.name.last}
)} 11 |
12 | 13 |
14 | ) 15 | } 16 | 17 | const styles = { 18 | wrapper: { 19 | display: "grid", 20 | gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", 21 | } 22 | } -------------------------------------------------------------------------------- /07_state/src/Person.jsx: -------------------------------------------------------------------------------- 1 | export const Person = () => { 2 | return ( 3 | <> 4 |

Person

5 | 6 | ); 7 | } -------------------------------------------------------------------------------- /07_state/src/main.css: -------------------------------------------------------------------------------- 1 | :root {} -------------------------------------------------------------------------------- /07_state/src/main.jsx: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import ReactDOM from 'react-dom/client' 3 | import App from './App.jsx' 4 | import './main.css' 5 | import 'bootstrap/dist/css/bootstrap.css' 6 | 7 | ReactDOM.createRoot(document.getElementById('root')).render( 8 | 9 | 10 | , 11 | ) 12 | -------------------------------------------------------------------------------- /07_state/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /08_props/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /08_props/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /08_props/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/08_props/README.md -------------------------------------------------------------------------------- /08_props/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /08_props/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.3.3", 19 | "@types/react-dom": "^18.3.0", 20 | "@vitejs/plugin-react-swc": "^3.3.2", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /08_props/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { People } from "./People" 3 | 4 | function App() { 5 | const [people, setPeople] = useState([ 6 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 7 | { id: 1, name: { first: "Monica", last: "Geller" } }, 8 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 9 | { id: 3, name: { first: "Rachel", last: "Green" } }, 10 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 11 | ]); 12 | const [numberToFetch, setNumberToFetch] = useState(10); 13 | const [gender, setGender] = useState("all"); 14 | return ( 15 | <> 16 |
17 |
18 |
19 |
20 |
21 | 22 | setNumberToFetch(e.target.value)} type="number" id="numberToFetch" value={numberToFetch} /> 23 |
24 |
25 | 26 | 31 |
32 |
33 | 34 | {people ? :

No people to show

} 35 |
36 |
37 | Copyright © Us.com {(new Date()).getFullYear()} 38 |
39 | 40 | ) 41 | 42 | function fetchPeople(numberToFetch, gender) { 43 | const url = `https://randomuser.me/api/?results=${numberToFetch}&gender=${gender}` 44 | fetch(url) 45 | .then(res => res.json()) 46 | .then(res => res.results) 47 | .then(people => people.map((p, i) => ({ ...p, id: i }))) 48 | .then(people => setPeople(people)) 49 | .catch(err => console.error("Error fetching", err)) 50 | } 51 | } 52 | 53 | export default App 54 | -------------------------------------------------------------------------------- /08_props/src/People.css: -------------------------------------------------------------------------------- 1 | .People {} -------------------------------------------------------------------------------- /08_props/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | import './People.css' 4 | 5 | export function People({ people }) { 6 | return ( 7 |
8 |

People

9 |
10 | {people.map(person => )} 11 |
12 |
13 | ) 14 | } 15 | 16 | const styles = { 17 | wrapper: { 18 | display: "grid", 19 | gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", 20 | } 21 | } -------------------------------------------------------------------------------- /08_props/src/Person.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | export const Person = ({ first, last, email, cell, imageSrc }) => { 3 | console.log(first, last, email, cell, imageSrc) 4 | return ( 5 | <> 6 |

Person

7 | 8 | ); 9 | } -------------------------------------------------------------------------------- /08_props/src/main.css: -------------------------------------------------------------------------------- 1 | :root {} -------------------------------------------------------------------------------- /08_props/src/main.jsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | import App from './App.jsx' 3 | import './main.css' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /08_props/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /09_composition/.eslintrc.cjs: -------------------------------------------------------------------------------- 1 | module.exports = { 2 | root: true, 3 | env: { browser: true, es2020: true }, 4 | extends: [ 5 | 'eslint:recommended', 6 | 'plugin:react/recommended', 7 | 'plugin:react/jsx-runtime', 8 | 'plugin:react-hooks/recommended', 9 | ], 10 | ignorePatterns: ['dist', '.eslintrc.cjs'], 11 | parserOptions: { ecmaVersion: 'latest', sourceType: 'module' }, 12 | settings: { react: { version: '18.2' } }, 13 | plugins: ['react-refresh'], 14 | rules: { 15 | 'react-refresh/only-export-components': [ 16 | 'warn', 17 | { allowConstantExport: true }, 18 | ], 19 | }, 20 | } 21 | -------------------------------------------------------------------------------- /09_composition/.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | npm-debug.log* 5 | yarn-debug.log* 6 | yarn-error.log* 7 | pnpm-debug.log* 8 | lerna-debug.log* 9 | 10 | node_modules 11 | dist 12 | dist-ssr 13 | *.local 14 | 15 | # Editor directories and files 16 | .vscode/* 17 | !.vscode/extensions.json 18 | .idea 19 | .DS_Store 20 | *.suo 21 | *.ntvs* 22 | *.njsproj 23 | *.sln 24 | *.sw? 25 | -------------------------------------------------------------------------------- /09_composition/README.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/rapPayne/hands_on_react/ed0851d208e52c46463fe9dcd8a71faf6c84cead/09_composition/README.md -------------------------------------------------------------------------------- /09_composition/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vite + React 8 | 9 | 10 |
11 | 12 | 13 | 14 | -------------------------------------------------------------------------------- /09_composition/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "demo", 3 | "private": true, 4 | "version": "0.0.0", 5 | "type": "module", 6 | "scripts": { 7 | "dev": "vite", 8 | "build": "vite build", 9 | "lint": "eslint . --ext js,jsx --report-unused-disable-directives --max-warnings 0", 10 | "preview": "vite preview" 11 | }, 12 | "dependencies": { 13 | "bootstrap": "^5.3.3", 14 | "react": "^18.3.1", 15 | "react-dom": "^18.3.1" 16 | }, 17 | "devDependencies": { 18 | "@types/react": "^18.3.3", 19 | "@types/react-dom": "^18.3.0", 20 | "@vitejs/plugin-react-swc": "^3.3.2", 21 | "eslint": "^8.45.0", 22 | "eslint-plugin-react": "^7.32.2", 23 | "eslint-plugin-react-hooks": "^4.6.0", 24 | "eslint-plugin-react-refresh": "^0.4.3", 25 | "vite": "^4.4.5" 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /09_composition/src/App.jsx: -------------------------------------------------------------------------------- 1 | import { useState } from 'react' 2 | import { People } from "./People" 3 | import { PeopleQuery } from './PeopleQuery'; 4 | 5 | function App() { 6 | const [people, setPeople] = useState([ 7 | { id: 0, name: { first: "Chandler", last: "Bing" } }, 8 | { id: 1, name: { first: "Monica", last: "Geller" } }, 9 | { id: 2, name: { first: "Phoebe", last: "Buffay" } }, 10 | { id: 3, name: { first: "Rachel", last: "Green" } }, 11 | { id: 4, name: { first: "Joey", last: "Tribiani" } }, 12 | ]); 13 | const [numberToFetch, setNumberToFetch] = useState(10); 14 | const [gender, setGender] = useState("all"); 15 | return ( 16 | <> 17 |
18 |
19 |
20 | 21 | {people ? :

No people to show

} 22 |
23 |
24 | Copyright © Us.com {(new Date()).getFullYear()} 25 |
26 | 27 | ) 28 | 29 | function fetchPeople(numberToFetch, gender) { 30 | const url = `https://randomuser.me/api/?results=${numberToFetch}&gender=${gender}` 31 | fetch(url) 32 | .then(res => res.json()) 33 | .then(res => res.results) 34 | .then(people => people.map((p, i) => ({ ...p, id: i }))) 35 | .then(people => setPeople(people)) 36 | .catch(err => console.error("Error fetching", err)) 37 | } 38 | } 39 | 40 | export default App 41 | -------------------------------------------------------------------------------- /09_composition/src/People.css: -------------------------------------------------------------------------------- 1 | .People {} -------------------------------------------------------------------------------- /09_composition/src/People.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import { Person } from "./Person"; 3 | import './People.css' 4 | 5 | export function People({ people }) { 6 | return ( 7 |
8 |

People

9 |
10 | {people.map(person => )} 11 |
12 |
13 | ) 14 | } 15 | 16 | const styles = { 17 | wrapper: { 18 | display: "grid", 19 | gridTemplateColumns: "repeat(auto-fit, minmax(250px, 1fr))", 20 | } 21 | } -------------------------------------------------------------------------------- /09_composition/src/PeopleQuery.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | 3 | export const PeopleQuery = ({ gender, setGender, numberToFetch, setNumberToFetch, fetchPeople }) => { 4 | return ( 5 |
6 |
7 | 8 | setNumberToFetch(e.target.value)} type="number" id="numberToFetch" value={numberToFetch} /> 9 |
10 |
11 | 12 | 17 |
18 | 19 |
20 | ) 21 | } -------------------------------------------------------------------------------- /09_composition/src/Person.css: -------------------------------------------------------------------------------- 1 | .Person { 2 | background-color: bisque; 3 | margin: 10px; 4 | 5 | &>h2 { 6 | text-align: center; 7 | } 8 | 9 | &>img { 10 | width: 100%; 11 | border-radius: 50%; 12 | } 13 | 14 | &>p { 15 | padding: 0 10px; 16 | margin: 0; 17 | overflow: hidden; 18 | 19 | &>span:first-child { 20 | font-weight: bold; 21 | padding-right: 5px; 22 | } 23 | 24 | &>span {} 25 | } 26 | } -------------------------------------------------------------------------------- /09_composition/src/Person.jsx: -------------------------------------------------------------------------------- 1 | /* eslint-disable react/prop-types */ 2 | import "./Person.css"; 3 | 4 | export const Person = ({ first, last, email, cell, imageSrc }) => { 5 | // console.log(first, last, email, cell, imageSrc) 6 | return ( 7 |
8 |

{first} {last}

9 | {first} 10 |

Email:{email}

11 |

Cell:{cell}

12 |
13 | ); 14 | } -------------------------------------------------------------------------------- /09_composition/src/main.css: -------------------------------------------------------------------------------- 1 | :root {} -------------------------------------------------------------------------------- /09_composition/src/main.jsx: -------------------------------------------------------------------------------- 1 | import ReactDOM from 'react-dom/client' 2 | import App from './App.jsx' 3 | import './main.css' 4 | import 'bootstrap/dist/css/bootstrap.css' 5 | 6 | ReactDOM.createRoot(document.getElementById('root')).render( 7 | 8 | ) 9 | -------------------------------------------------------------------------------- /09_composition/vite.config.js: -------------------------------------------------------------------------------- 1 | import { defineConfig } from 'vite' 2 | import react from '@vitejs/plugin-react-swc' 3 | 4 | // https://vitejs.dev/config/ 5 | export default defineConfig({ 6 | plugins: [react()], 7 | }) 8 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2020 Rap Payne 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # hands_on_react 2 | To support my Hands On React course on O'Reilly Learning Portal 3 | 4 | # Connect with Rap 5 | I'd love to connect with you to provide help learning React. Please reach out. 6 | - X: [@RapPayne](https://X.com/RapPayne) 7 | - LinkedIn: [RapPayne](https://www.linkedin.com/in/rappayne/) 8 | - Github: [RapPayne](https://github.com/rapPayne) 9 | - Web: AgileGadgets.com 10 | 11 | We've created solutions at certain checkpoints. 12 | 1. [Create a React app](02_create_a_react_app) 13 | 2. [Creating a React component](03_creating_a_react_component) 14 | 1. [JSX](04_JSX) 15 | 1. [Events](05_events) 16 | 1. [Styling](06_styling) 17 | 1. [State](07_state) 18 | 5. [Props](08_props) 19 | 1. [Composition](09_composition) 20 | 21 | ## How to run these examples 22 | 1. cd to the folder of your choice 23 | 3. npm install 24 | 4. npm run dev 25 | 26 | Of course you can make any changes to the code you like at this point. Just save a file and the app will refresh in your browser. 27 | 28 | ## Exercises 29 | (No exercise for section 1 - Intro to React). 30 | 31 | ### 2. Create a React app 32 | This one is simple. Just `npm create vite` to create the application. 33 | 34 | ### 3. Creating a React component 35 | 1. Make two component files: People.jsx and Person.jsx. 36 | 2. Add the data-reading code to People.jsx. If you like, you can copy the fetcher from assets/fetchPeople.js. 37 | 38 | ### 4. JSX 39 | Now, drawing the people in App.js doesn't seem clean. We should be drawing the people in People.js. But we have to get those people into PeopleList somehow. Props will do the trick! 40 | 3. Add JSX to App.jsx to host People.js. 41 | 4. Add JSX to People.js to host Person.js. 42 | 5. Add a button to People.js to fetch 10 people. Console.log() them. 43 | 44 | ### 5. Events 45 | 1. Make the delete button call your new deletePerson function 46 | 47 | ### 6. Styling 48 | 1. Add in CSS to format the Person. Put this in Person.css and import it. 49 | 2. Use JavaScript styles for the flexbox layout. 50 | 51 | ### 7. State 52 | 1. In People, add a function to delete a single person from the people list. Make it call setState() with the new list of people. 53 | 1. Add a delete button to each Person. 54 | 1. Pass the function from People into Person via a prop. 55 | 1. Make the delete button call your new deletePerson function 56 | 57 | ### 8. Props 58 | Now, drawing the people in App.js doesn't seem clean. We should be drawing the people in People.js. But we have to get those people into PeopleList somehow. Props will do the trick! 59 | 1. Pass people from People.js down to Person.js 60 | 61 | ### 9. Composition 62 | -------------------------------------------------------------------------------- /ReactCheatSheet.md: -------------------------------------------------------------------------------- 1 | # Quick ref - JavaScript things you commonly need for React 2 | 3 | ## Destructuring 4 | ```JavaScript 5 | const array = ['one', 'two', 'three', 'four']; 6 | const object = { id: 123, name: 'Jo', email: 'jo@gmail.com' }; 7 | ``` 8 | 9 | ```JavaScript 10 | const [first, second, , , last ] = array; 11 | 12 | ``` 13 | // or 14 | let {first, last} = { 15 | first: 'Cyan', 16 | last: 'Hall' 17 | } 18 | 19 | ## Ways to create a function 20 | const getName = user => user.name 21 | const funcName = name => { 22 | // do something 23 | return name 24 | } 25 | 26 | ## Arrays 27 | // Delete at index 28 | array.splice(index, 1) 29 | 30 | // Insert at index 31 | array.splice(index, 0, newItem) 32 | 33 | // check exist 34 | [1, 2, 3].includes(3) // true 35 | 36 | // find index 37 | [1, 2, 3].indexOf(3) // 2; return -1 if not found 38 | 39 | // concat 40 | let array3 = array1.concat(array2) // [1].concat([2]) is [1, 2] 41 | 42 | // new array 43 | let array4 = [1, 2, 3, 4, 5].slice(2, 4) // [3, 4] 44 | 45 | ## Iterating arrays 46 | for (const i of [1, 2, 3]) { 47 | console.log(i) 48 | } 49 | // 1 50 | // 2 51 | // 3 52 | 53 | for (const [index, value] of ['Cyan', 'Hall', '.com'].entries()) { 54 | console.log(index, value) 55 | } 56 | // 0 "Cyan" 57 | // 1 "Hall" 58 | // 2 ".com" 59 | 60 | const obj = {part1: 'Cyan', part2: 'Hall', part3: '.com'}; 61 | for (const key in obj) { 62 | console.log(key, obj[key]) 63 | } 64 | // or 65 | for (const [key, value] of Object.entries(obj)) { 66 | console.log(key, value) 67 | } 68 | // part1 Cyan 69 | // part2 Hall 70 | // part3 .com 71 | 72 | ### But you may not need to iterate 73 | const numbers = [1, 2, 3] 74 | numbers.map(n => n * 2) // [2, 4, 6] 75 | numbers.filter(n => n % 2 === 0) // [2] 76 | numbers.reduce((prev, next) => prev + next, 0) // 6 77 | numbers.find(n => n > 2) // 3 78 | 79 | ## Async 80 | funcName('test') 81 | .then(result => { 82 | // ... 83 | }) 84 | .catch(error => { 85 | // ... 86 | }) 87 | .finally(() => { 88 | // ... 89 | }) 90 | 91 | ## async/await 92 | const funcName = async () => { 93 | const data = await fetchData() 94 | return data 95 | } 96 | 97 | await funcName() 98 | 99 | ## Spread 100 | const options = { 101 | ...defaults, 102 | show: true 103 | } 104 | 105 | const array3 = [ 106 | ...array1, 107 | ...array2, 108 | 'newItem' 109 | ] 110 | 111 | ## Template strings 112 | let text = ( `cat 113 | dog 114 | nickelodeon` 115 | ); 116 | Template Literals can accept expressions, as well: 117 | 118 | let today = new Date(); 119 | let text = `The time and date is ${today.toLocaleString()}`; 120 | 121 | ## Modules 122 | To share JS from another file we need two things: 123 | 1. To export from one file 124 | 2. To import from the other 125 | 126 | file1.js 127 | ```JavaScript 128 | export let name = 'Jo'; // "named" export 129 | let age = 27; 130 | export default age; // "default" export 131 | ``` 132 | 133 | file2.js 134 | ```JavaScript 135 | import age from './file1.js'; // default import 136 | import { name } from './file1.js'; // named import 137 | ``` 138 | -------------------------------------------------------------------------------- /assets/Person.css: -------------------------------------------------------------------------------- 1 | .Person { 2 | background-color: var(--secondary); 3 | flex: 1 1 100px; 4 | margin: 10px; 5 | 6 | & div.imageAndName { 7 | position: relative; 8 | 9 | & img { 10 | width: 100%; 11 | position: relative; 12 | } 13 | 14 | & p { 15 | position: absolute; 16 | top: 0; 17 | padding: 5px; 18 | margin: 0; 19 | font-size: 1.5em; 20 | width: 100%; 21 | text-align: center; 22 | background: linear-gradient(var(--background), rgba(255, 255, 255, 0)); 23 | } 24 | } 25 | 26 | & div.details { 27 | & div { 28 | & p { 29 | margin: 0 10px 0 10px; 30 | padding: 0; 31 | } 32 | 33 | & p:first-of-type { 34 | text-transform: uppercase; 35 | font-weight: bold; 36 | 37 | & ::after { 38 | content: ":"; 39 | } 40 | } 41 | 42 | & p:last-of-type {} 43 | } 44 | } 45 | } -------------------------------------------------------------------------------- /assets/Person.html: -------------------------------------------------------------------------------- 1 |
2 |
3 | {person?.name.first} 4 |

{person?.name.first} {person?.name.last}

5 |
6 |
7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 25 | 26 | 27 |
Cell{person?.cell}
Email{person?.email}
Address 20 | {person?.location.street.number} {person?.location.street.name}
21 | {person?.location?.city}, 22 | {person?.location?.state} 23 | {person?.location?.postcode} 24 |
28 |
29 |
-------------------------------------------------------------------------------- /assets/README.md: -------------------------------------------------------------------------------- 1 | ### Assets 2 | This folder contains pre-written code and other things to make the creation of your React app simpler. 3 | 4 | -------------------------------------------------------------------------------- /assets/fetchPeople.js: -------------------------------------------------------------------------------- 1 | export function fetchPeople(number=10, nat=['gb','fi', 'de', 'us'], gender="all") { 2 | const url=`https://randomuser.me/api/?results=${number}&nat=${nat.join(",")}&gender=${gender}`; 3 | console.log(url); 4 | return fetch(url) 5 | .then(res => res.json()) 6 | .then(res => res.results) 7 | .catch(err => console.error("Error fetching people", err)); 8 | } --------------------------------------------------------------------------------