├── .gitignore ├── README.md ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt ├── src ├── App.css ├── App.js ├── App.test.js ├── Dish.js ├── ExampleAPICall.js ├── index.css ├── index.js ├── logo.svg ├── reportWebVitals.js └── setupTests.js └── yarn.lock /.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 | .eslintcache 21 | 22 | npm-debug.log* 23 | yarn-debug.log* 24 | yarn-error.log* 25 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Intro to React and React Hooks ✨ 2 | 3 | **The full recording of the workshop can be found [here!](https://www.youtube.com/watch?v=UDPS_VCx4yE&ab_channel=HacktheNorth)** 4 | 5 | In this introductory React workshop, you will extend your frontend development knowledge beyond basic HTML, CSS, and JavaScript. React is one of the most popular and widely used frontend libraries and can be used to scale and elevate web applications. We will cover React fundamentals and set you up with everything you need to know to start your first React project. As a bonus, we’ll be covering React Hooks, which transform and simplify the way we write React applications! 6 | 7 | For this workshop, we'll be building a fun little **restaurant app**! 8 | 9 | ## Workshop Structure 10 | 11 | ### [Basic React 1: Importing React](https://github.com/lesliexin/intro-to-react-workshop/tree/03774739d59d9638c2b7b7ccff8469139e3019ca) 12 | 13 | Here we'll set-up a basic HTMl scaffold and import the React and ReactDOM libraries. 14 | 15 | ### [Basic React 2: Creating a component](https://github.com/lesliexin/intro-to-react-workshop/tree/5cbffd709bbb57e7b98305e2e60e230de80aac3e) 16 | 17 | Next we'll write our first React functional component, `App`. We'll also learn about the `React.createElement()` and `React.render()` functions. 18 | 19 | ### [Basic React 3: Creating child components](https://github.com/lesliexin/intro-to-react-workshop/tree/09b35e69b2cb92887e915cc9522caf1d478f26f6) 20 | 21 | Afterwards, we'll create child components and render them in the parent component we created earlier. 👨‍👦 22 | 23 | ### [Basic React 4: Passing Props](https://github.com/lesliexin/intro-to-react-workshop/tree/b167ea0ea0772d22e5dfb367c4620d95991ea87f) 24 | 25 | Finally, we'll learn how to pass unique data from a parent component to a child component through React props. 26 | 27 | ### [Create React App 1: Default installation](https://github.com/lesliexin/intro-to-react-workshop/tree/a055739ed3036879b3720dfce193aa5d4848bc50) 28 | 29 | What's Create React App? Oh nothing much, only the fastest and easiest way to start a new React project for a beginner! 🚀 30 | 31 | ### [Create React App 2: Custom CSS and removing default code](https://github.com/lesliexin/intro-to-react-workshop/tree/6d665d9228af801c0b4cac2fd237696e151d2cc6) 32 | 33 | This isn't a workshop about CSS...but let's add some to make our app a little bit easier to look at. (No commentary is meant from the CSS here). We'll also rip out some of the guts of the default Create React App so we have a clean slate to start with. 34 | 35 | ### [Create React App 3: Creating the Dish component](https://github.com/lesliexin/intro-to-react-workshop/tree/00b12f126dc4905065211faf1987a49b55fc0e2d) 36 | 37 | Let's recreate the restaurant app we made in part one. But let's make a class component and add state this time! We'll also learn how our lives get easier when we can write JSX. 38 | 39 | ### [Create React App 4: Adding interactivity](https://github.com/lesliexin/intro-to-react-workshop/tree/c7279a82d436aaf371dc5f1d3eb3654be30dfaf9) 40 | 41 | We'll add in some user input to our app here to make it more interesting! 42 | 43 | ### [React Hooks 1: Refactoring code to use hooks](https://github.com/lesliexin/intro-to-react-workshop/tree/a6a2ef6583f1561ce95b527b06f0b966d4185a08) 44 | 45 | 🎣 What are React Hooks and why are they awesome? Let's try them out and see... 46 | 47 | ### [React Hooks 2: Adding additional price prop](https://github.com/lesliexin/intro-to-react-workshop/tree/05b3f473e2b423510aab0a0b0e0015e21e34d76a) 48 | 49 | We'll add some additional data to our app to make it seem closer to a real restaurant app. 50 | 51 | ### [Extending React: Example API call with cards API](https://github.com/lesliexin/intro-to-react-workshop/tree/f1c3ec4ed99ba448b14aad82e301c8fb53e917d2) 52 | 53 | Here's an example API call unrelated to the restaurant app, using the free deck of cards API. 54 | 55 | ## How to run the application 56 | 57 | ### Part 1: Basic React 58 | 59 | Simply open the `index.html` file in your browser. 60 | 61 | ### Part 2 and 3: Create React App and React Hooks 62 | 63 | In the project directory, you can run: 64 | 65 | #### `yarn start` or `npm start` 66 | 67 | This runs the app in the development mode.\ 68 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 69 | The page will reload if you make edits.\ 70 | You will also see any lint errors in the console. 71 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "htn-restaurant", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.11.4", 7 | "@testing-library/react": "^11.1.0", 8 | "@testing-library/user-event": "^12.1.10", 9 | "react": "^17.0.1", 10 | "react-dom": "^17.0.1", 11 | "react-scripts": "4.0.1", 12 | "web-vitals": "^0.2.4" 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": [ 22 | "react-app", 23 | "react-app/jest" 24 | ] 25 | }, 26 | "browserslist": { 27 | "production": [ 28 | ">0.2%", 29 | "not dead", 30 | "not op_mini all" 31 | ], 32 | "development": [ 33 | "last 1 chrome version", 34 | "last 1 firefox version", 35 | "last 1 safari version" 36 | ] 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesliexin/intro-to-react-workshop/2dc0a07623564140571c94b4f7a27f271cc5eff4/public/favicon.ico -------------------------------------------------------------------------------- /public/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 12 | 13 | 17 | 18 | 27 | HTN Restaurant 28 | 29 | 30 | 31 |
32 | 42 | 43 | 44 | -------------------------------------------------------------------------------- /public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesliexin/intro-to-react-workshop/2dc0a07623564140571c94b4f7a27f271cc5eff4/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/lesliexin/intro-to-react-workshop/2dc0a07623564140571c94b4f7a27f271cc5eff4/public/logo512.png -------------------------------------------------------------------------------- /public/manifest.json: -------------------------------------------------------------------------------- 1 | { 2 | "short_name": "HTN Restaurant", 3 | "name": "Intro to React + Hooks Workshop", 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.css: -------------------------------------------------------------------------------- 1 | .App { 2 | text-align: center; 3 | } 4 | 5 | .App-logo { 6 | height: 40vmin; 7 | pointer-events: none; 8 | } 9 | 10 | @media (prefers-reduced-motion: no-preference) { 11 | .App-logo { 12 | animation: App-logo-spin infinite 20s linear; 13 | } 14 | } 15 | 16 | .App-header { 17 | background-color: #282c34; 18 | min-height: 100vh; 19 | display: flex; 20 | flex-direction: column; 21 | align-items: center; 22 | justify-content: center; 23 | font-size: calc(10px + 2vmin); 24 | color: white; 25 | } 26 | 27 | .App-link { 28 | color: #61dafb; 29 | } 30 | 31 | @keyframes App-logo-spin { 32 | from { 33 | transform: rotate(0deg); 34 | } 35 | to { 36 | transform: rotate(360deg); 37 | } 38 | } 39 | -------------------------------------------------------------------------------- /src/App.js: -------------------------------------------------------------------------------- 1 | import { Dish } from "./Dish"; 2 | 3 | function App() { 4 | return ( 5 |
6 |

HTN Restaurant

7 | 8 | 9 | 10 |
11 | ); 12 | } 13 | 14 | export default App; 15 | -------------------------------------------------------------------------------- /src/App.test.js: -------------------------------------------------------------------------------- 1 | import { render, screen } from '@testing-library/react'; 2 | import App from './App'; 3 | 4 | test('renders learn react link', () => { 5 | render(); 6 | const linkElement = screen.getByText(/learn react/i); 7 | expect(linkElement).toBeInTheDocument(); 8 | }); 9 | -------------------------------------------------------------------------------- /src/Dish.js: -------------------------------------------------------------------------------- 1 | import React, {useState} from "react" 2 | 3 | export const Dish = (props) => { 4 | const [counter, setCounter] = useState(0); 5 | 6 | const addDish = () => { 7 | setCounter(counter+1); 8 | }; 9 | 10 | return ( 11 |
12 |

{props.name}

13 |

Price: ${props.price}

14 |

Amount: {counter}

15 | 16 |
17 | ); 18 | }; 19 | -------------------------------------------------------------------------------- /src/ExampleAPICall.js: -------------------------------------------------------------------------------- 1 | import React, { useEffect, useState } from "react"; 2 | 3 | export const App = () => { 4 | const [cardData, setCardData] = useState([]); 5 | const [deckId, setDeckId] = useState(""); 6 | const CARD_API_URL = 7 | "https://deckofcardsapi.com/api/deck/new/shuffle/?deck_count=1"; 8 | 9 | // executes once at the beginning of each render 10 | useEffect(() => { 11 | getDeck(); 12 | }, []); 13 | 14 | // making API call using fetch to get deck id 15 | const getDeck = () => { 16 | fetch(CARD_API_URL) 17 | .then((response) => response.json()) 18 | .then((data) => { 19 | setDeckId(data.deck_id); 20 | }) 21 | .catch((e) => { 22 | console.log(e); 23 | }); 24 | }; 25 | 26 | // making API call using fetch to get a card 27 | const getCards = () => { 28 | fetch(`https://deckofcardsapi.com/api/deck/${deckId}/draw/?count=2`) 29 | .then((response) => response.json()) 30 | .then((data) => { 31 | setCardData([data.cards[0].image, data.cards[1].image]); 32 | }) 33 | .catch((e) => { 34 | console.log(e); 35 | }); 36 | }; 37 | 38 | return ( 39 |
40 |

Get a deck of cards from this API

41 |

click a button to display two drawn cards

42 | 43 | {cardData.length > 0 && ( 44 |
45 | card 1 46 | card 2 47 |
48 | )} 49 |
50 | ); 51 | } 52 | -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | body { 2 | margin: 0; 3 | font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", "Roboto", "Oxygen", 4 | "Ubuntu", "Cantarell", "Fira Sans", "Droid Sans", "Helvetica Neue", 5 | sans-serif; 6 | -webkit-font-smoothing: antialiased; 7 | -moz-osx-font-smoothing: grayscale; 8 | background: #f8d7d0; 9 | display: flex; 10 | flex-direction: column; 11 | align-items: center; 12 | } 13 | 14 | code { 15 | font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New", 16 | monospace; 17 | } 18 | 19 | button { 20 | padding: 0.7em 1.4em; 21 | margin: 0 0.3em 0.3em 0; 22 | border: none; 23 | box-sizing: border-box; 24 | font-weight: bold; 25 | background: #caeefd; 26 | text-align: center; 27 | position: relative; 28 | border-radius: 20px; 29 | } 30 | 31 | button:hover { 32 | background: #b2def0; 33 | } 34 | 35 | button:active { 36 | outline: none; 37 | background: #95cae0; 38 | } 39 | 40 | button:focus { 41 | outline: none; 42 | } 43 | 44 | .Dish { 45 | background: #ffffff; 46 | padding: 30px; 47 | margin-bottom: 20px; 48 | box-shadow: 0px 4px 15px rgba(0, 0, 0, 0.15); 49 | border-radius: 20px; 50 | } 51 | 52 | h1 { 53 | font-style: normal; 54 | font-weight: bold; 55 | font-size: 50px; 56 | color: #004d85; 57 | } 58 | 59 | h2 { 60 | margin-top: 0; 61 | } 62 | 63 | p { 64 | font-weight: bold; 65 | color: #696969; 66 | } 67 | -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | import './index.css'; 4 | import App from './App'; 5 | import reportWebVitals from './reportWebVitals'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | 14 | // If you want to start measuring performance in your app, pass a function 15 | // to log results (for example: reportWebVitals(console.log)) 16 | // or send to an analytics endpoint. Learn more: https://bit.ly/CRA-vitals 17 | reportWebVitals(); 18 | -------------------------------------------------------------------------------- /src/logo.svg: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /src/reportWebVitals.js: -------------------------------------------------------------------------------- 1 | const reportWebVitals = onPerfEntry => { 2 | if (onPerfEntry && onPerfEntry instanceof Function) { 3 | import('web-vitals').then(({ getCLS, getFID, getFCP, getLCP, getTTFB }) => { 4 | getCLS(onPerfEntry); 5 | getFID(onPerfEntry); 6 | getFCP(onPerfEntry); 7 | getLCP(onPerfEntry); 8 | getTTFB(onPerfEntry); 9 | }); 10 | } 11 | }; 12 | 13 | export default reportWebVitals; 14 | -------------------------------------------------------------------------------- /src/setupTests.js: -------------------------------------------------------------------------------- 1 | // jest-dom adds custom jest matchers for asserting on DOM nodes. 2 | // allows you to do things like: 3 | // expect(element).toHaveTextContent(/react/i) 4 | // learn more: https://github.com/testing-library/jest-dom 5 | import '@testing-library/jest-dom'; 6 | --------------------------------------------------------------------------------