├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── assets └── default-image.jpeg ├── data.js ├── index.css ├── index.js └── tutorial ├── 1-useState ├── final │ ├── 1-error-example.js │ ├── 2-useState-basics.js │ ├── 3-useState-array.js │ ├── 4-useState-object.js │ └── 5-useState-counter.js └── setup │ ├── 1-error-example.js │ ├── 2-useState-basics.js │ ├── 3-useState-array.js │ ├── 4-useState-object.js │ └── 5-useState-counter.js ├── 10-prop-types ├── final │ ├── Product.js │ ├── index.js │ └── testing.js └── setup │ ├── Product.js │ └── index.js ├── 11-react-router ├── ReactRouterInfo.md ├── final │ ├── About.js │ ├── Error.js │ ├── Home.js │ ├── Navbar.js │ ├── People.js │ ├── Person.js │ └── index.js └── setup │ ├── About.js │ ├── Error.js │ ├── Home.js │ ├── Navbar.js │ ├── People.js │ ├── Person.js │ └── index.js ├── 12-memo-useMemo-useCallback ├── final │ └── index.js └── setup │ └── index.js ├── 2-useEffect ├── final │ ├── 1-useEffect-basics.js │ ├── 2-useEffect-cleanup.js │ └── 3-useEffect-fetch-data.js └── setup │ ├── 1-useEffect-basics.js │ ├── 2-useEffect-cleanup.js │ └── 3-useEffect-fetch-data.js ├── 3-conditional-rendering ├── final │ ├── 1-multiple-returns.js │ ├── 2-short-circuit.js │ └── 3-show-hide.js └── setup │ ├── 1-multiple-returns.js │ ├── 2-short-circuit.js │ └── 3-show-hide.js ├── 4-forms ├── final │ ├── 1-controlled-inputs.js │ └── 2-multiple-inputs.js └── setup │ ├── 1-controlled-inputs.js │ └── 2-multiple-inputs.js ├── 5-useRef ├── final │ └── 1-useRef-basics.js └── setup │ └── 1-useRef-basics.js ├── 6-useReducer ├── final │ ├── Modal.js │ ├── index.js │ └── reducer.js └── setup │ ├── Modal.js │ └── index.js ├── 7-prop-drilling ├── final │ └── 1-prop-drilling.js └── setup │ └── 1-prop-drilling.js ├── 8-useContext ├── final │ └── 1-context-api.js └── setup │ └── 1-context-api.js └── 9-custom-hooks ├── final ├── 1-fetch-example.js └── 2-useFetch.js └── setup ├── 1-fetch-example.js └── 2-useFetch.js /.gitignore: -------------------------------------------------------------------------------- 1 | # See https://help.github.com/articles/ignoring-files/ for more about ignoring files. 2 | 3 | # dependencies 4 | /node_modules 5 | /.pnp 6 | .pnp.js 7 | 8 | # testing 9 | /coverage 10 | 11 | # production 12 | /build 13 | 14 | # misc 15 | .DS_Store 16 | .env.local 17 | .env.development.local 18 | .env.test.local 19 | .env.production.local 20 | 21 | npm-debug.log* 22 | yarn-debug.log* 23 | yarn-error.log* 24 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ## Corresponding Projects 2 | 3 | #### useState 4 | 5 | 1. Birthday Reminder 6 | 7 | #### useEffect and Conditional Rendering 8 | 9 | 2. Tours 10 | 3. Reviews 11 | 4. Accordion 12 | 5. Menu 13 | 6. Tabs 14 | 7. Slider 15 | 16 | #### Forms 17 | 18 | 8. Lorem Ipsum Generator 19 | 9. Color Shades Generator 20 | 10. Grocery Bud 21 | 22 | #### useRef 23 | 24 | 11. Navbar 25 | 26 | #### useContext 27 | 28 | 12. Modal and Sidebar 29 | 13. Stripe Menus 30 | 31 | #### useReducer and useContext 32 | 33 | 14. Cart 34 | 35 | #### React Router 36 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "tutorial", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^4.2.4", 7 | "@testing-library/react": "^9.5.0", 8 | "@testing-library/user-event": "^7.2.1", 9 | "react": "^16.13.1", 10 | "react-dom": "^16.13.1", 11 | "react-router-dom": "^5.2.0", 12 | "react-scripts": "3.4.3" 13 | }, 14 | "scripts": { 15 | "start": "react-scripts start", 16 | "build": "react-scripts build", 17 | "test": "react-scripts test", 18 | "eject": "react-scripts eject" 19 | }, 20 | "eslintConfig": { 21 | "extends": "react-app" 22 | }, 23 | "browserslist": { 24 | "production": [ 25 | ">0.2%", 26 | "not dead", 27 | "not op_mini all" 28 | ], 29 | "development": [ 30 | "last 1 chrome version", 31 | "last 1 firefox version", 32 | "last 1 safari version" 33 | ] 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-advanced-2020/19f34c9a3290feb87b00be0385cd84605ee8e649/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | React App 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-advanced-2020/19f34c9a3290feb87b00be0385cd84605ee8e649/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-advanced-2020/19f34c9a3290feb87b00be0385cd84605ee8e649/public/logo512.png -------------------------------------------------------------------------------- /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 | "src": "logo192.png", 12 | "type": "image/png", 13 | "sizes": "192x192" 14 | }, 15 | { 16 | "src": "logo512.png", 17 | "type": "image/png", 18 | "sizes": "512x512" 19 | } 20 | ], 21 | "start_url": ".", 22 | "display": "standalone", 23 | "theme_color": "#000000", 24 | "background_color": "#ffffff" 25 | } 26 | -------------------------------------------------------------------------------- /public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | function App() { 3 | return ( 4 |
5 |

Advanced Tutorial

6 |
7 | ) 8 | } 9 | 10 | export default App 11 | -------------------------------------------------------------------------------- /src/assets/default-image.jpeg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/john-smilga/react-advanced-2020/19f34c9a3290feb87b00be0385cd84605ee8e649/src/assets/default-image.jpeg -------------------------------------------------------------------------------- /src/data.js: -------------------------------------------------------------------------------- 1 | export const data = [ 2 | { id: 1, name: 'john' }, 3 | { id: 2, name: 'peter' }, 4 | { id: 3, name: 'susan' }, 5 | { id: 4, name: 'anna' }, 6 | ]; 7 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | /* 2 | =============== 3 | Variables 4 | =============== 5 | */ 6 | 7 | :root { 8 | /* dark shades of primary color*/ 9 | --clr-primary-1: hsl(205, 86%, 17%); 10 | --clr-primary-2: hsl(205, 77%, 27%); 11 | --clr-primary-3: hsl(205, 72%, 37%); 12 | --clr-primary-4: hsl(205, 63%, 48%); 13 | /* primary/main color */ 14 | --clr-primary-5: hsl(205, 78%, 60%); 15 | /* lighter shades of primary color */ 16 | --clr-primary-6: hsl(205, 89%, 70%); 17 | --clr-primary-7: hsl(205, 90%, 76%); 18 | --clr-primary-8: hsl(205, 86%, 81%); 19 | --clr-primary-9: hsl(205, 90%, 88%); 20 | --clr-primary-10: hsl(205, 100%, 96%); 21 | /* darkest grey - used for headings */ 22 | --clr-grey-1: hsl(209, 61%, 16%); 23 | --clr-grey-2: hsl(211, 39%, 23%); 24 | --clr-grey-3: hsl(209, 34%, 30%); 25 | --clr-grey-4: hsl(209, 28%, 39%); 26 | /* grey used for paragraphs */ 27 | --clr-grey-5: hsl(210, 22%, 49%); 28 | --clr-grey-6: hsl(209, 23%, 60%); 29 | --clr-grey-7: hsl(211, 27%, 70%); 30 | --clr-grey-8: hsl(210, 31%, 80%); 31 | --clr-grey-9: hsl(212, 33%, 89%); 32 | --clr-grey-10: hsl(210, 36%, 96%); 33 | --clr-white: #fff; 34 | --clr-red-dark: hsl(360, 67%, 44%); 35 | --clr-red-light: hsl(360, 71%, 66%); 36 | --clr-green-dark: hsl(125, 67%, 44%); 37 | --clr-green-light: hsl(125, 71%, 66%); 38 | --clr-black: #222; 39 | --transition: all 0.3s linear; 40 | --spacing: 0.1rem; 41 | --radius: 0.25rem; 42 | --light-shadow: 0 5px 15px rgba(0, 0, 0, 0.1); 43 | --dark-shadow: 0 5px 15px rgba(0, 0, 0, 0.4); 44 | --max-width: 1170px; 45 | --fixed-width: 450px; 46 | --clr-orange-1: hsl(12, 83%, 98%); 47 | --clr-orange-2: hsl(14, 91%, 95%); 48 | --clr-orange-3: hsl(12, 89%, 89%); 49 | --clr-orange-4: hsl(13, 87%, 82%); 50 | --clr-orange-5: hsl(13, 88%, 68%); 51 | --clr-orange-6: hsl(13, 88%, 55%); 52 | --clr-orange-7: hsl(13, 74%, 49%); 53 | --clr-orange-8: hsl(13, 74%, 33%); 54 | --clr-orange-9: hsl(13, 73%, 25%); 55 | --clr-orange-10: hsl(13, 73%, 16%); 56 | } 57 | /* 58 | =============== 59 | Global Styles 60 | =============== 61 | */ 62 | 63 | *, 64 | ::after, 65 | ::before { 66 | margin: 0; 67 | padding: 0; 68 | box-sizing: border-box; 69 | } 70 | body { 71 | font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, Oxygen, 72 | Ubuntu, Cantarell, 'Open Sans', 'Helvetica Neue', sans-serif; 73 | background: var(--clr-grey-10); 74 | color: var(--clr-grey-1); 75 | line-height: 1.5; 76 | font-size: 0.875rem; 77 | } 78 | ul { 79 | list-style-type: none; 80 | } 81 | a { 82 | text-decoration: none; 83 | } 84 | h1, 85 | h2, 86 | h3, 87 | h4 { 88 | letter-spacing: var(--spacing); 89 | text-transform: capitalize; 90 | line-height: 1.25; 91 | margin-bottom: 0.75rem; 92 | } 93 | h1 { 94 | font-size: 3rem; 95 | } 96 | h2 { 97 | font-size: 2rem; 98 | } 99 | h3 { 100 | font-size: 1.25rem; 101 | } 102 | h4 { 103 | font-size: 0.875rem; 104 | } 105 | p { 106 | margin-bottom: 1.25rem; 107 | color: var(--clr-grey-5); 108 | } 109 | @media screen and (min-width: 800px) { 110 | h1 { 111 | font-size: 4rem; 112 | } 113 | h2 { 114 | font-size: 2.5rem; 115 | } 116 | h3 { 117 | font-size: 1.75rem; 118 | } 119 | h4 { 120 | font-size: 1rem; 121 | } 122 | body { 123 | font-size: 1rem; 124 | } 125 | h1, 126 | h2, 127 | h3, 128 | h4 { 129 | line-height: 1; 130 | } 131 | } 132 | /* global classes */ 133 | 134 | /* section */ 135 | .section, 136 | .container { 137 | width: 90vw; 138 | margin: 0 auto; 139 | max-width: var(--max-width); 140 | } 141 | 142 | @media screen and (min-width: 992px) { 143 | .section { 144 | width: 95vw; 145 | } 146 | } 147 | 148 | .container { 149 | text-align: center; 150 | margin-top: 5rem; 151 | } 152 | .btn { 153 | display: inline-block; 154 | background: var(--clr-primary-5); 155 | color: var(--clr-white); 156 | padding: 0.25rem 0.75rem; 157 | border-radius: var(--radius); 158 | border-color: transparent; 159 | text-transform: capitalize; 160 | font-size: 1rem; 161 | letter-spacing: var(--spacing); 162 | margin-top: 2rem; 163 | margin-left: 0.5rem; 164 | margin-right: 0.5rem; 165 | cursor: pointer; 166 | transition: var(--transition); 167 | } 168 | .btn:hover { 169 | background: var(--clr-primary-1); 170 | color: var(--clr-primary-5); 171 | } 172 | .item { 173 | background: var(--clr-white); 174 | display: flex; 175 | justify-content: space-between; 176 | max-width: var(--fixed-width); 177 | margin: 2rem auto; 178 | align-items: center; 179 | border-radius: var(--radius); 180 | } 181 | .item button, 182 | .item a { 183 | background: transparent; 184 | border-color: transparent; 185 | color: var(--clr-primary-5); 186 | letter-spacing: var(--spacing); 187 | cursor: pointer; 188 | } 189 | .item { 190 | padding: 1rem 2rem; 191 | } 192 | .item h4 { 193 | margin-bottom: 0; 194 | } 195 | .item p { 196 | margin-bottom: 0; 197 | } 198 | .modal { 199 | position: absolute; 200 | top: 2rem; 201 | left: 50%; 202 | transform: translateX(-50%); 203 | background: var(--clr-white); 204 | display: inline-block; 205 | padding: 0.25rem 1rem; 206 | border-radius: var(--radius); 207 | text-transform: capitalize; 208 | } 209 | 210 | .modal p { 211 | margin-bottom: 0; 212 | color: var(--clr-red-dark); 213 | } 214 | .form { 215 | background: var(--clr-white); 216 | max-width: var(--fixed-width); 217 | margin: 0 auto; 218 | margin-bottom: 4rem; 219 | padding: 1rem 2rem; 220 | border-radius: var(--radius); 221 | } 222 | .form input { 223 | background: var(--clr-grey-10); 224 | border-color: transparent; 225 | border-radius: var(--radius); 226 | padding: 0.25rem 0.5rem; 227 | } 228 | .form-control { 229 | margin: 0.5rem 0; 230 | display: grid; 231 | grid-template-columns: 100px 1fr; 232 | align-items: center; 233 | } 234 | .form button { 235 | display: inline-block; 236 | background: var(--clr-black); 237 | color: var(--clr-white); 238 | border-color: transparent; 239 | margin-top: 1rem; 240 | letter-spacing: var(--spacing); 241 | padding: 0.15rem 0.25rem; 242 | text-transform: capitalize; 243 | border-radius: var(--radius); 244 | cursor: pointer; 245 | } 246 | .nav-links { 247 | max-width: var(--fixed-width); 248 | margin: 0 auto; 249 | margin-bottom: 4rem; 250 | display: grid; 251 | grid-template-columns: repeat(3, 1fr); 252 | } 253 | .nav-links a { 254 | color: var(--clr-grey-5); 255 | } 256 | .users { 257 | display: grid; 258 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 259 | gap: 2rem; 260 | margin: 3rem auto; 261 | } 262 | .users li { 263 | width: 100%; 264 | display: flex; 265 | align-items: center; 266 | background: var(--clr-white); 267 | padding: 1rem 2rem; 268 | border-radius: var(--radius); 269 | text-align: left; 270 | } 271 | .users img { 272 | width: 50px; 273 | height: 50px; 274 | border-radius: 50%; 275 | margin-right: 1rem; 276 | } 277 | .users h4 { 278 | margin-bottom: 0.15rem; 279 | } 280 | .users a { 281 | color: var(--clr-grey-5); 282 | text-transform: capitalize; 283 | } 284 | 285 | .products { 286 | margin: 4rem 0; 287 | display: grid; 288 | gap: 2rem; 289 | } 290 | @media screen and (min-width: 576px) { 291 | .products { 292 | grid-template-columns: repeat(auto-fit, minmax(300px, 1fr)); 293 | } 294 | } 295 | .product { 296 | background: var(--clr-white); 297 | border-radius: var(--radius); 298 | } 299 | .product img { 300 | border-top-left-radius: var(--radius); 301 | border-top-right-radius: var(--radius); 302 | 303 | width: 100%; 304 | height: 15rem; 305 | object-fit: cover; 306 | } 307 | .product h4 { 308 | margin-top: 1rem; 309 | } 310 | 311 | .product button { 312 | margin-bottom: 1rem; 313 | background: var(--clr-primary-5); 314 | border-radius: var(--radius); 315 | border-color: transparent; 316 | color: var(--clr-white); 317 | padding: 0.25rem 0.5rem; 318 | text-transform: capitalize; 319 | cursor: pointer; 320 | } 321 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | ReactDOM.render( 7 | 8 | 9 | , 10 | document.getElementById('root') 11 | ); 12 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/final/1-error-example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ErrorExample = () => { 4 | let title = 'random title'; 5 | 6 | const handleClick = () => { 7 | title = 'hello people'; 8 | console.log(title); 9 | }; 10 | return ( 11 | 12 |

{title}

13 | 16 |
17 | ); 18 | }; 19 | 20 | export default ErrorExample; 21 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/final/2-useState-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // starts with use 3 | // component must be uppercase 4 | // invoke inside function/component body 5 | // don't call hooks conditonally 6 | 7 | const UseStateBasics = () => { 8 | // console.log(useState()); 9 | // const value = useState()[0]; 10 | // const handler = useState()[1]; 11 | // console.log(value, handler); 12 | 13 | const [text, setText] = useState('random title'); 14 | const handleClick = () => { 15 | if (text === 'random title') { 16 | setText('hello world'); 17 | } else { 18 | setText('random title'); 19 | } 20 | }; 21 | 22 | return ( 23 | 24 |

{text}

25 | 28 |
29 | ); 30 | }; 31 | 32 | export default UseStateBasics; 33 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/final/3-useState-array.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { data } from '../../../data'; 3 | const UseStateArray = () => { 4 | const [people, setPeople] = React.useState(data); 5 | 6 | const removeItem = (id) => { 7 | let newPeople = people.filter((person) => person.id !== id); 8 | setPeople(newPeople); 9 | }; 10 | return ( 11 | <> 12 | {people.map((person) => { 13 | const { id, name } = person; 14 | return ( 15 |
16 |

{name}

17 | 18 |
19 | ); 20 | })} 21 | 24 | 25 | ); 26 | }; 27 | 28 | export default UseStateArray; 29 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/final/4-useState-object.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const UseStateObject = () => { 4 | const [person, setPerson] = useState({ 5 | name: 'peter', 6 | age: 24, 7 | message: 'random message', 8 | }); 9 | 10 | // const [name,setName] = useState('peter') 11 | // const [age,setAge] = useState(24) 12 | // const [message,setMessage] = useState('random message') 13 | 14 | const changeMessage = () => { 15 | setPerson({ ...person, message: 'hello world' }); 16 | // setMessage('hello world') 17 | }; 18 | 19 | return ( 20 | <> 21 |

{person.name}

22 |

{person.age}

23 |

{person.message}

24 | 27 | 28 | ); 29 | }; 30 | 31 | export default UseStateObject; 32 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/final/5-useState-counter.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const UseStateCounter = () => { 4 | const [value, setValue] = useState(0); 5 | 6 | const reset = () => { 7 | setValue(0); 8 | }; 9 | 10 | const complexIncrease = () => { 11 | setTimeout(() => { 12 | // setValue(value + 1); 13 | setValue((prevState) => { 14 | return prevState + 1; 15 | }); 16 | }, 2000); 17 | }; 18 | 19 | return ( 20 | <> 21 |
22 |

regular counter

23 |

{value}

24 | 27 | 30 | 33 |
34 |
35 |

more complex counter

36 |

{value}

37 | 40 |
41 | 42 | ); 43 | }; 44 | 45 | export default UseStateCounter; 46 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/setup/1-error-example.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const ErrorExample = () => { 4 | return

useState error example

; 5 | }; 6 | 7 | export default ErrorExample; 8 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/setup/2-useState-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const UseStateBasics = () => { 4 | return

useState basic example

; 5 | }; 6 | 7 | export default UseStateBasics; 8 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/setup/3-useState-array.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { data } from '../../../data'; 3 | 4 | const UseStateArray = () => { 5 | return

useState array example

; 6 | }; 7 | 8 | export default UseStateArray; 9 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/setup/4-useState-object.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const UseStateObject = () => { 4 | return

useState object example

; 5 | }; 6 | 7 | export default UseStateObject; 8 | -------------------------------------------------------------------------------- /src/tutorial/1-useState/setup/5-useState-counter.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | const UseStateCounter = () => { 4 | return

useState counter example

; 5 | }; 6 | 7 | export default UseStateCounter; 8 | -------------------------------------------------------------------------------- /src/tutorial/10-prop-types/final/Product.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | import defaultImage from '../../../assets/default-image.jpeg'; 4 | const Product = ({ image, name, price }) => { 5 | const url = image && image.url; 6 | return ( 7 |
8 | {name 9 |

{name}

10 |

${price || 3.99}

11 |
12 | ); 13 | }; 14 | 15 | Product.propTypes = { 16 | image: PropTypes.object.isRequired, 17 | name: PropTypes.string.isRequired, 18 | price: PropTypes.number.isRequired, 19 | }; 20 | // Product.defaultProps = { 21 | // name: 'default name', 22 | // price: 3.99, 23 | // image: defaultImage, 24 | // }; 25 | 26 | export default Product; 27 | -------------------------------------------------------------------------------- /src/tutorial/10-prop-types/final/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Product from './Product' 3 | import { useFetch } from '../../9-custom-hooks/final/2-useFetch' 4 | // import defaultImage from '../../../assets/default-image.jpeg'; 5 | 6 | // ATTENTION!!!!!!!!!! 7 | // I SWITCHED TO PERMANENT DOMAIN 8 | const url = 'https://course-api.com/react-prop-types-example' 9 | 10 | const Index = () => { 11 | const { products } = useFetch(url) 12 | return ( 13 |
14 |

products

15 | {/* */} 16 |
17 | {products.map((product) => { 18 | return 19 | })} 20 |
21 |
22 | ) 23 | } 24 | 25 | export default Index 26 | -------------------------------------------------------------------------------- /src/tutorial/10-prop-types/final/testing.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import PropTypes from 'prop-types'; 3 | 4 | const testing = (props) => { 5 | return
; 6 | }; 7 | 8 | testing.propTypes = { 9 | name: PropTypes.array.isRequired, 10 | }; 11 | 12 | export default testing; 13 | -------------------------------------------------------------------------------- /src/tutorial/10-prop-types/setup/Product.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Product = () => { 4 | return
single product
; 5 | }; 6 | 7 | export default Product; 8 | -------------------------------------------------------------------------------- /src/tutorial/10-prop-types/setup/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react' 2 | import Product from './Product' 3 | import { useFetch } from '../../9-custom-hooks/final/2-useFetch' 4 | 5 | // ATTENTION!!!!!!!!!! 6 | // I SWITCHED TO PERMANENT DOMAIN 7 | const url = 'https://course-api.com/react-prop-types-example' 8 | 9 | const Index = () => { 10 | const { products } = useFetch(url) 11 | return ( 12 |
13 |

products

14 |
15 | {products.map((product) => { 16 | return 17 | })} 18 |
19 |
20 | ) 21 | } 22 | 23 | export default Index 24 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/ReactRouterInfo.md: -------------------------------------------------------------------------------- 1 | !(react router)[https://reactrouter.com/web/guides/quick-start] 2 | 3 | ``` 4 | npm install react-router-dom 5 | 6 | ``` 7 | 8 | "react-router-dom": "^5.2.0" 9 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
6 |

About Page

7 |
8 | ); 9 | }; 10 | 11 | export default About; 12 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | const Error = () => { 4 | return ( 5 |
6 |

Error Page

7 | 8 | Back Home 9 | 10 |
11 | ); 12 | }; 13 | 14 | export default Error; 15 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

Home Page

7 |

shake and bake

8 |
9 | ); 10 | }; 11 | 12 | export default Home; 13 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | const Navbar = () => { 4 | return ( 5 | 18 | ); 19 | }; 20 | 21 | export default Navbar; 22 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/People.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { data } from '../../../data'; 3 | import { Link } from 'react-router-dom'; 4 | const People = () => { 5 | const [people, setPeople] = useState(data); 6 | return ( 7 |
8 |

People Page

9 | {people.map((person) => { 10 | return ( 11 |
12 |

{person.name}

13 | Learn More 14 |
15 | ); 16 | })} 17 |
18 | ); 19 | }; 20 | 21 | export default People; 22 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/Person.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { data } from '../../../data'; 3 | import { Link, useParams } from 'react-router-dom'; 4 | const Person = () => { 5 | const [name, setName] = useState('default name'); 6 | const { id } = useParams(); 7 | 8 | useEffect(() => { 9 | const newPerson = data.find((person) => person.id === parseInt(id)); 10 | setName(newPerson.name); 11 | }, []); 12 | return ( 13 |
14 |

{name}

15 | 16 | Back To People 17 | 18 |
19 | ); 20 | }; 21 | 22 | export default Person; 23 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/final/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // react router 3 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 4 | // pages 5 | import Home from './Home'; 6 | import About from './About'; 7 | import People from './People'; 8 | import Error from './Error'; 9 | import Person from './Person'; 10 | // navbar 11 | import Navbar from './Navbar'; 12 | const ReactRouterSetup = () => { 13 | return ( 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | 26 | }> 27 | 28 | 29 | 30 | 31 | 32 | ); 33 | }; 34 | 35 | export default ReactRouterSetup; 36 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/About.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const About = () => { 4 | return ( 5 |
6 |

About Page

7 |
8 | ); 9 | }; 10 | 11 | export default About; 12 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/Error.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | const Error = () => { 4 | return ( 5 |
6 |

Error Page

7 |
8 | ); 9 | }; 10 | 11 | export default Error; 12 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/Home.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | 3 | const Home = () => { 4 | return ( 5 |
6 |

Home Page

7 |
8 | ); 9 | }; 10 | 11 | export default Home; 12 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/Navbar.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import { Link } from 'react-router-dom'; 3 | const Navbar = () => { 4 | return ; 5 | }; 6 | 7 | export default Navbar; 8 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/People.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { data } from '../../../data'; 3 | import { Link } from 'react-router-dom'; 4 | const People = () => { 5 | const [people, setPeople] = useState(data); 6 | return ( 7 |
8 |

People Page

9 | {people.map((person) => { 10 | return ( 11 |
12 |

{person.name}

13 |
14 | ); 15 | })} 16 |
17 | ); 18 | }; 19 | 20 | export default People; 21 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/Person.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | import { data } from '../../../data'; 3 | import { Link, useParams } from 'react-router-dom'; 4 | const Person = () => { 5 | return ( 6 |
7 |

person

8 |
9 | ); 10 | }; 11 | 12 | export default Person; 13 | -------------------------------------------------------------------------------- /src/tutorial/11-react-router/setup/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | // react router 3 | import { BrowserRouter as Router, Route, Switch } from 'react-router-dom'; 4 | // pages 5 | import Home from './Home'; 6 | import About from './About'; 7 | import People from './People'; 8 | import Error from './Error'; 9 | import Person from './Person'; 10 | // navbar 11 | import Navbar from './Navbar'; 12 | const ReactRouterSetup = () => { 13 | return

react router

; 14 | }; 15 | 16 | export default ReactRouterSetup; 17 | -------------------------------------------------------------------------------- /src/tutorial/12-memo-useMemo-useCallback/final/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useCallback, useMemo } from 'react' 2 | import { useFetch } from '../../9-custom-hooks/final/2-useFetch' 3 | 4 | // ATTENTION!!!!!!!!!! 5 | // I SWITCHED TO PERMANENT DOMAIN 6 | const url = 'https://course-api.com/javascript-store-products' 7 | 8 | // every time props or state changes, component re-renders 9 | const calculateMostExpensive = (data) => { 10 | return ( 11 | data.reduce((total, item) => { 12 | const price = item.fields.price 13 | if (price >= total) { 14 | total = price 15 | } 16 | return total 17 | }, 0) / 100 18 | ) 19 | } 20 | const Index = () => { 21 | const { products } = useFetch(url) 22 | const [count, setCount] = useState(0) 23 | const [cart, setCart] = useState(0) 24 | 25 | const addToCart = useCallback(() => { 26 | setCart(cart + 1) 27 | }, [cart]) 28 | 29 | const mostExpensive = useMemo(() => calculateMostExpensive(products), [ 30 | products, 31 | ]) 32 | return ( 33 | <> 34 |

Count : {count}

35 | 38 |

cart : {cart}

39 |

Most Expensive : ${mostExpensive}

40 | 41 | 42 | ) 43 | } 44 | 45 | const BigList = React.memo(({ products, addToCart }) => { 46 | // useEffect(() => { 47 | // console.count('hello from big list'); 48 | // }); 49 | 50 | return ( 51 |
52 | {products.map((product) => { 53 | return ( 54 | 59 | ) 60 | })} 61 |
62 | ) 63 | }) 64 | 65 | const SingleProduct = ({ fields, addToCart }) => { 66 | let { name, price } = fields 67 | price = price / 100 68 | const image = fields.image[0].url 69 | 70 | // useEffect(() => { 71 | // console.count('hello from product'); 72 | // }); 73 | return ( 74 |
75 | {name} 76 |

{name}

77 |

${price}

78 | 79 |
80 | ) 81 | } 82 | export default Index 83 | -------------------------------------------------------------------------------- /src/tutorial/12-memo-useMemo-useCallback/setup/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect, useCallback, useMemo } from 'react' 2 | import { useFetch } from '../../9-custom-hooks/final/2-useFetch' 3 | 4 | // ATTENTION!!!!!!!!!! 5 | // I SWITCHED TO PERMANENT DOMAIN 6 | const url = 'https://course-api.com/javascript-store-products' 7 | 8 | // every time props or state changes, component re-renders 9 | 10 | const Index = () => { 11 | const { products } = useFetch(url) 12 | const [count, setCount] = useState(0) 13 | 14 | return ( 15 | <> 16 |

Count : {count}

17 | 20 | 21 | 22 | ) 23 | } 24 | 25 | const BigList = ({ products }) => { 26 | return ( 27 |
28 | {products.map((product) => { 29 | return 30 | })} 31 |
32 | ) 33 | } 34 | 35 | const SingleProduct = ({ fields }) => { 36 | let { name, price } = fields 37 | price = price / 100 38 | const image = fields.image[0].url 39 | 40 | return ( 41 |
42 | {name} 43 |

{name}

44 |

${price}

45 |
46 | ) 47 | } 48 | export default Index 49 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/final/1-useEffect-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | // by default runs after every re-render 3 | // cleanup function 4 | // second parameter 5 | const UseEffectBasics = () => { 6 | const [value, setValue] = useState(0); 7 | useEffect(() => { 8 | console.log('call useEffect'); 9 | if (value > 0) { 10 | document.title = `New Messages(${value})`; 11 | } 12 | }); 13 | 14 | console.log('render component'); 15 | return ( 16 | <> 17 |

{value}

18 | 21 | 22 | ); 23 | }; 24 | 25 | export default UseEffectBasics; 26 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/final/2-useEffect-cleanup.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | // cleanup function 4 | // second argument 5 | 6 | const UseEffectCleanup = () => { 7 | const [size, setSize] = useState(window.innerWidth); 8 | 9 | const checkSize = () => { 10 | setSize(window.innerWidth); 11 | }; 12 | 13 | useEffect(() => { 14 | console.log('useEffect'); 15 | window.addEventListener('resize', checkSize); 16 | return () => { 17 | console.log('cleanup'); 18 | window.removeEventListener('resize', checkSize); 19 | }; 20 | }, []); 21 | console.log('render'); 22 | return ( 23 | <> 24 |

window

25 |

{size} PX

26 | 27 | ); 28 | }; 29 | 30 | export default UseEffectCleanup; 31 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/final/3-useEffect-fetch-data.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const url = 'https://api.github.com/users'; 4 | 5 | // second argument 6 | 7 | const UseEffectFetchData = () => { 8 | const [users, setUsers] = useState([]); 9 | 10 | const getUsers = async () => { 11 | const response = await fetch(url); 12 | const users = await response.json(); 13 | setUsers(users); 14 | // console.log(users); 15 | }; 16 | 17 | useEffect(() => { 18 | getUsers(); 19 | }, []); 20 | return ( 21 | <> 22 |

github users

23 | 37 | 38 | ); 39 | }; 40 | 41 | export default UseEffectFetchData; 42 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/setup/1-useEffect-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | // by default runs after every re-render 3 | // cleanup function 4 | // second parameter 5 | const UseEffectBasics = () => { 6 | return

useEffect Basics

; 7 | }; 8 | 9 | export default UseEffectBasics; 10 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/setup/2-useEffect-cleanup.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | // cleanup function 4 | // second argument 5 | 6 | const UseEffectCleanup = () => { 7 | return

useEffect cleanup

; 8 | }; 9 | 10 | export default UseEffectCleanup; 11 | -------------------------------------------------------------------------------- /src/tutorial/2-useEffect/setup/3-useEffect-fetch-data.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const url = 'https://api.github.com/users'; 4 | 5 | const UseEffectFetchData = () => { 6 | return

fetch data

; 7 | }; 8 | 9 | export default UseEffectFetchData; 10 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/final/1-multiple-returns.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | const url = 'https://api.github.com/users/QuincyLarson'; 3 | const MultipleReturns = () => { 4 | const [isLoading, setIsLoading] = useState(true); 5 | const [isError, setIsError] = useState(false); 6 | const [user, setUser] = useState('default user'); 7 | 8 | useEffect(() => { 9 | fetch(url) 10 | .then((resp) => { 11 | if (resp.status >= 200 && resp.status <= 299) { 12 | return resp.json(); 13 | } else { 14 | setIsLoading(false); 15 | setIsError(true); 16 | throw new Error(resp.statusText); 17 | } 18 | }) 19 | .then((user) => { 20 | const { login } = user; 21 | setUser(login); 22 | setIsLoading(false); 23 | }) 24 | .catch((error) => console.log(error)); 25 | }, []); 26 | 27 | if (isLoading) { 28 | return ( 29 |
30 |

Loading...

31 |
32 | ); 33 | } 34 | if (isError) { 35 | return ( 36 |
37 |

Error....

38 |
39 | ); 40 | } 41 | return ( 42 |
43 |

{user}

44 |
45 | ); 46 | }; 47 | 48 | export default MultipleReturns; 49 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/final/2-short-circuit.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // short-circuit evaluation 3 | // ternary operator 4 | 5 | const ShortCircuit = () => { 6 | const [text, setText] = useState(''); 7 | const [isError, setIsError] = useState(false); 8 | // const firstValue = text || 'hello world'; 9 | // const secondValue = text && 'hello world'; 10 | 11 | return ( 12 | <> 13 | {/*

{firstValue}

14 |

value : {secondValue}

*/} 15 | {/* {if(){console.log('hello world')}} */} 16 |

{text || 'john doe'}

17 | 20 | {isError &&

Error...

} 21 | {isError ? ( 22 |

there is an error...

23 | ) : ( 24 |
25 |

there is no error

26 |
27 | )} 28 | 29 | ); 30 | }; 31 | 32 | export default ShortCircuit; 33 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/final/3-show-hide.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const ShowHide = () => { 4 | const [show, setShow] = useState(false); 5 | return ( 6 | <> 7 | 10 | {show && } 11 | 12 | ); 13 | }; 14 | 15 | const Item = () => { 16 | const [size, setSize] = useState(window.innerWidth); 17 | const checkSize = () => { 18 | setSize(window.innerWidth); 19 | }; 20 | useEffect(() => { 21 | window.addEventListener('resize', checkSize); 22 | return () => { 23 | window.removeEventListener('resize', checkSize); 24 | }; 25 | }, []); 26 | 27 | return ( 28 |
29 |

Window

30 |

size : {size}

31 |
32 | ); 33 | }; 34 | 35 | export default ShowHide; 36 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/setup/1-multiple-returns.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | const url = 'https://api.github.com/users/QuincyLarson'; 3 | const MultipleReturns = () => { 4 | return

multiple returns

; 5 | }; 6 | 7 | export default MultipleReturns; 8 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/setup/2-short-circuit.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // short-circuit evaluation 3 | // ternary operator 4 | 5 | const ShortCircuit = () => { 6 | // const firstValue = text || 'hello world'; 7 | // const secondValue = text && 'hello world'; 8 | 9 | return

short circuit

; 10 | }; 11 | 12 | export default ShortCircuit; 13 | -------------------------------------------------------------------------------- /src/tutorial/3-conditional-rendering/setup/3-show-hide.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react'; 2 | 3 | const ShowHide = () => { 4 | return

show/hide

; 5 | }; 6 | 7 | export default ShowHide; 8 | -------------------------------------------------------------------------------- /src/tutorial/4-forms/final/1-controlled-inputs.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // JS 3 | // const input = document.getElementById('myText'); 4 | // const inputValue = input.value 5 | // React 6 | // value, onChange 7 | 8 | const ControlledInputs = () => { 9 | const [firstName, setFirstName] = useState(''); 10 | const [email, setEmail] = useState(''); 11 | const [people, setPeople] = useState([]); 12 | 13 | const handleSubmit = (e) => { 14 | e.preventDefault(); 15 | if (firstName && email) { 16 | const person = { id: new Date().getTime().toString(), firstName, email }; 17 | console.log(person); 18 | setPeople((people) => { 19 | return [...people, person]; 20 | }); 21 | setFirstName(''); 22 | setEmail(''); 23 | } else { 24 | console.log('empty values'); 25 | } 26 | }; 27 | return ( 28 | <> 29 |
30 |
31 |
32 | 33 | setFirstName(e.target.value)} 39 | /> 40 |
41 |
42 | 43 | setEmail(e.target.value)} 49 | /> 50 |
51 | 52 |
53 | {people.map((person, index) => { 54 | const { id, firstName, email } = person; 55 | return ( 56 |
57 |

{firstName}

58 |

{email}

59 |
60 | ); 61 | })} 62 |
63 | 64 | ); 65 | }; 66 | 67 | export default ControlledInputs; 68 | -------------------------------------------------------------------------------- /src/tutorial/4-forms/final/2-multiple-inputs.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // JS 3 | // const input = document.getElementById('myText'); 4 | // const inputValue = input.value 5 | // React 6 | // value, onChange 7 | 8 | const ControlledInputs = () => { 9 | const [person, setPerson] = useState({ firstName: '', email: '', age: '' }); 10 | const [people, setPeople] = useState([]); 11 | const handleChange = (e) => { 12 | const name = e.target.name; 13 | const value = e.target.value; 14 | setPerson({ ...person, [name]: value }); 15 | }; 16 | const handleSubmit = (e) => { 17 | e.preventDefault(); 18 | if (person.firstName && person.email && person.age) { 19 | const newPerson = { ...person, id: new Date().getTime().toString() }; 20 | setPeople([...people, newPerson]); 21 | setPerson({ firstName: '', email: '', age: '' }); 22 | } 23 | }; 24 | return ( 25 | <> 26 |
27 |
28 |
29 | 30 | 37 |
38 |
39 | 40 | 47 |
48 |
49 | 50 | 57 |
58 | 61 |
62 |
63 |
64 | {people.map((person) => { 65 | const { id, firstName, email, age } = person; 66 | return ( 67 |
68 |

{firstName}

69 |

{email}

70 |

{age}

71 |
72 | ); 73 | })} 74 |
75 | 76 | ); 77 | }; 78 | 79 | export default ControlledInputs; 80 | -------------------------------------------------------------------------------- /src/tutorial/4-forms/setup/1-controlled-inputs.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // JS 3 | // const input = document.getElementById('myText'); 4 | // const inputValue = input.value 5 | // React 6 | // value, onChange 7 | 8 | const ControlledInputs = () => { 9 | return

controlled inputs

; 10 | }; 11 | 12 | export default ControlledInputs; 13 | -------------------------------------------------------------------------------- /src/tutorial/4-forms/setup/2-multiple-inputs.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | // JS 3 | // const input = document.getElementById('myText'); 4 | // const inputValue = input.value 5 | // React 6 | // value, onChange 7 | // dynamic object keys 8 | 9 | const ControlledInputs = () => { 10 | const [firstName, setFirstName] = useState(''); 11 | const [email, setEmail] = useState(''); 12 | const [people, setPeople] = useState([]); 13 | 14 | const handleSubmit = (e) => { 15 | e.preventDefault(); 16 | if (firstName && email) { 17 | const person = { id: new Date().getTime().toString(), firstName, email }; 18 | console.log(person); 19 | setPeople((people) => { 20 | return [...people, person]; 21 | }); 22 | setFirstName(''); 23 | setEmail(''); 24 | } else { 25 | console.log('empty values'); 26 | } 27 | }; 28 | return ( 29 | <> 30 |
31 |
32 |
33 | 34 | setFirstName(e.target.value)} 40 | /> 41 |
42 |
43 | 44 | setEmail(e.target.value)} 50 | /> 51 |
52 | 53 |
54 | {people.map((person, index) => { 55 | const { id, firstName, email } = person; 56 | return ( 57 |
58 |

{firstName}

59 |

{email}

60 |
61 | ); 62 | })} 63 |
64 | 65 | ); 66 | }; 67 | 68 | export default ControlledInputs; 69 | -------------------------------------------------------------------------------- /src/tutorial/5-useRef/final/1-useRef-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | 3 | // preserves value 4 | // DOES NOT trigger re-render 5 | // target DOM nodes/elements 6 | 7 | const UseRefBasics = () => { 8 | const refContainer = useRef(null); 9 | 10 | const handleSubmit = (e) => { 11 | e.preventDefault(); 12 | console.log(refContainer.current.value); 13 | }; 14 | useEffect(() => { 15 | console.log(refContainer.current); 16 | refContainer.current.focus(); 17 | }); 18 | 19 | return ( 20 | <> 21 |
22 |
23 | 24 |
25 | 26 |
27 | 28 | ); 29 | }; 30 | 31 | export default UseRefBasics; 32 | -------------------------------------------------------------------------------- /src/tutorial/5-useRef/setup/1-useRef-basics.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useRef } from 'react'; 2 | 3 | // preserves value 4 | // DOES NOT trigger re-render 5 | // target DOM nodes/elements 6 | 7 | const UseRefBasics = () => { 8 | return

useRef

; 9 | }; 10 | 11 | export default UseRefBasics; 12 | -------------------------------------------------------------------------------- /src/tutorial/6-useReducer/final/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | const Modal = ({ modalContent, closeModal }) => { 4 | useEffect(() => { 5 | setTimeout(() => { 6 | closeModal(); 7 | }, 3000); 8 | }); 9 | return ( 10 |
11 |

{modalContent}

12 |
13 | ); 14 | }; 15 | 16 | export default Modal; 17 | -------------------------------------------------------------------------------- /src/tutorial/6-useReducer/final/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useReducer } from 'react'; 2 | import Modal from './Modal'; 3 | import { data } from '../../../data'; 4 | // reducer function 5 | import { reducer } from './reducer'; 6 | const defaultState = { 7 | people: [], 8 | isModalOpen: false, 9 | modalContent: '', 10 | }; 11 | const Index = () => { 12 | const [name, setName] = useState(''); 13 | const [state, dispatch] = useReducer(reducer, defaultState); 14 | const handleSubmit = (e) => { 15 | e.preventDefault(); 16 | if (name) { 17 | const newItem = { id: new Date().getTime().toString(), name }; 18 | dispatch({ type: 'ADD_ITEM', payload: newItem }); 19 | setName(''); 20 | } else { 21 | dispatch({ type: 'NO_VALUE' }); 22 | } 23 | }; 24 | const closeModal = () => { 25 | dispatch({ type: 'CLOSE_MODAL' }); 26 | }; 27 | return ( 28 | <> 29 | {state.isModalOpen && ( 30 | 31 | )} 32 |
33 |
34 | setName(e.target.value)} 38 | /> 39 |
40 | 41 |
42 | {state.people.map((person) => { 43 | return ( 44 |
45 |

{person.name}

46 | 53 |
54 | ); 55 | })} 56 | 57 | ); 58 | }; 59 | 60 | export default Index; 61 | -------------------------------------------------------------------------------- /src/tutorial/6-useReducer/final/reducer.js: -------------------------------------------------------------------------------- 1 | export const reducer = (state, action) => { 2 | if (action.type === 'ADD_ITEM') { 3 | const newPeople = [...state.people, action.payload]; 4 | return { 5 | ...state, 6 | people: newPeople, 7 | isModalOpen: true, 8 | modalContent: 'item added', 9 | }; 10 | } 11 | if (action.type === 'NO_VALUE') { 12 | return { ...state, isModalOpen: true, modalContent: 'please enter value' }; 13 | } 14 | if (action.type === 'CLOSE_MODAL') { 15 | return { ...state, isModalOpen: false }; 16 | } 17 | if (action.type === 'REMOVE_ITEM') { 18 | const newPeople = state.people.filter( 19 | (person) => person.id !== action.payload 20 | ); 21 | return { ...state, people: newPeople }; 22 | } 23 | throw new Error('no matching action type'); 24 | }; 25 | -------------------------------------------------------------------------------- /src/tutorial/6-useReducer/setup/Modal.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect } from 'react'; 2 | 3 | const Modal = () => { 4 | return
i'm modal
; 5 | }; 6 | 7 | export default Modal; 8 | -------------------------------------------------------------------------------- /src/tutorial/6-useReducer/setup/index.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useReducer } from 'react'; 2 | import Modal from './Modal'; 3 | import { data } from '../../../data'; 4 | // reducer function 5 | 6 | const Index = () => { 7 | return

useReducer

; 8 | }; 9 | 10 | export default Index; 11 | -------------------------------------------------------------------------------- /src/tutorial/7-prop-drilling/final/1-prop-drilling.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | import { data } from '../../../data'; 3 | // more components 4 | // fix - context api, redux (for more complex cases) 5 | 6 | const PropDrilling = () => { 7 | const [people, setPeople] = useState(data); 8 | const removePerson = (id) => { 9 | setPeople((people) => { 10 | return people.filter((person) => person.id !== id); 11 | }); 12 | }; 13 | return ( 14 |
15 |

prop drilling

16 | 17 |
18 | ); 19 | }; 20 | 21 | const List = ({ people, removePerson }) => { 22 | return ( 23 | <> 24 | {people.map((person) => { 25 | return ( 26 | 31 | ); 32 | })} 33 | 34 | ); 35 | }; 36 | 37 | const SinglePerson = ({ id, name, removePerson }) => { 38 | return ( 39 |
40 |

{name}

41 | 42 |
43 | ); 44 | }; 45 | 46 | export default PropDrilling; 47 | -------------------------------------------------------------------------------- /src/tutorial/7-prop-drilling/setup/1-prop-drilling.js: -------------------------------------------------------------------------------- 1 | import React, { useState } from 'react'; 2 | 3 | // more components 4 | // fix - context api, redux (for more complex cases) 5 | 6 | const PropDrilling = () => { 7 | return

prop drilling

; 8 | }; 9 | 10 | export default PropDrilling; 11 | -------------------------------------------------------------------------------- /src/tutorial/8-useContext/final/1-context-api.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import { data } from '../../../data'; 3 | // more components 4 | // fix - context api, redux (for more complex cases) 5 | 6 | const PersonContext = React.createContext(); 7 | // two components - Provider, Consumer 8 | 9 | const ContextAPI = () => { 10 | const [people, setPeople] = useState(data); 11 | const removePerson = (id) => { 12 | setPeople((people) => { 13 | return people.filter((person) => person.id !== id); 14 | }); 15 | }; 16 | return ( 17 | 18 |

Context API / useContext

19 | 20 |
21 | ); 22 | }; 23 | 24 | const List = () => { 25 | const mainData = useContext(PersonContext); 26 | console.log(mainData); 27 | return ( 28 | <> 29 | {mainData.people.map((person) => { 30 | return ; 31 | })} 32 | 33 | ); 34 | }; 35 | 36 | const SinglePerson = ({ id, name }) => { 37 | const { removePerson } = useContext(PersonContext); 38 | 39 | return ( 40 |
41 |

{name}

42 | 43 |
44 | ); 45 | }; 46 | 47 | export default ContextAPI; 48 | -------------------------------------------------------------------------------- /src/tutorial/8-useContext/setup/1-context-api.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useContext } from 'react'; 2 | import { data } from '../../../data'; 3 | // more components 4 | // fix - context api, redux (for more complex cases) 5 | 6 | const ContextAPI = () => { 7 | const [people, setPeople] = useState(data); 8 | const removePerson = (id) => { 9 | setPeople((people) => { 10 | return people.filter((person) => person.id !== id); 11 | }); 12 | }; 13 | return ( 14 | <> 15 |

prop drilling

16 | 17 | 18 | ); 19 | }; 20 | 21 | const List = ({ people, removePerson }) => { 22 | return ( 23 | <> 24 | {people.map((person) => { 25 | return ( 26 | 31 | ); 32 | })} 33 | 34 | ); 35 | }; 36 | 37 | const SinglePerson = ({ id, name, removePerson }) => { 38 | return ( 39 |
40 |

{name}

41 | 42 |
43 | ); 44 | }; 45 | 46 | export default ContextAPI; 47 | -------------------------------------------------------------------------------- /src/tutorial/9-custom-hooks/final/1-fetch-example.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { useFetch } from './2-useFetch' 3 | 4 | // ATTENTION!!!!!!!!!! 5 | // I SWITCHED TO PERMANENT DOMAIN 6 | const url = 'https://course-api.com/javascript-store-products' 7 | 8 | const Example = () => { 9 | const { loading, products } = useFetch(url) 10 | console.log(products) 11 | return ( 12 |
13 |

{loading ? 'loading...' : 'data'}

14 |
15 | ) 16 | } 17 | 18 | export default Example 19 | -------------------------------------------------------------------------------- /src/tutorial/9-custom-hooks/final/2-useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect, useCallback } from 'react'; 2 | 3 | export const useFetch = (url) => { 4 | const [loading, setLoading] = useState(true); 5 | const [products, setProducts] = useState([]); 6 | 7 | const getProducts = useCallback(async () => { 8 | const response = await fetch(url); 9 | const products = await response.json(); 10 | setProducts(products); 11 | setLoading(false); 12 | }, [url]); 13 | 14 | useEffect(() => { 15 | getProducts(); 16 | }, [url, getProducts]); 17 | return { loading, products }; 18 | }; 19 | -------------------------------------------------------------------------------- /src/tutorial/9-custom-hooks/setup/1-fetch-example.js: -------------------------------------------------------------------------------- 1 | import React, { useState, useEffect } from 'react' 2 | import { useFetch } from './2-useFetch' 3 | 4 | // ATTENTION!!!!!!!!!! 5 | // I SWITCHED TO PERMANENT DOMAIN 6 | const url = 'https://course-api.com/javascript-store-products' 7 | 8 | const Example = () => { 9 | const [loading, setLoading] = useState(true) 10 | const [products, setProducts] = useState([]) 11 | 12 | const getProducts = async () => { 13 | const response = await fetch(url) 14 | const products = await response.json() 15 | setProducts(products) 16 | setLoading(false) 17 | } 18 | 19 | useEffect(() => { 20 | getProducts() 21 | }, [url]) 22 | console.log(products) 23 | return ( 24 |
25 |

{loading ? 'loading...' : 'data'}

26 |
27 | ) 28 | } 29 | 30 | export default Example 31 | -------------------------------------------------------------------------------- /src/tutorial/9-custom-hooks/setup/2-useFetch.js: -------------------------------------------------------------------------------- 1 | import { useState, useEffect } from 'react'; 2 | 3 | export const useFetch = () => {}; 4 | --------------------------------------------------------------------------------