├── .gitignore ├── code-snippets └── snippet-1 │ ├── reactjs │ └── app.js │ └── vanilla-javascript │ ├── app.js │ └── index.html ├── examples └── example-1 │ ├── reactjs │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── public │ │ ├── favicon.ico │ │ ├── index.html │ │ ├── logo192.png │ │ ├── logo512.png │ │ ├── manifest.json │ │ └── robots.txt │ └── src │ │ ├── App.js │ │ ├── index.css │ │ └── index.js │ └── vanilla-javascript │ ├── app.js │ ├── index.html │ └── styles.css └── exercises └── questions-answers.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | .vscode -------------------------------------------------------------------------------- /code-snippets/snippet-1/reactjs/app.js: -------------------------------------------------------------------------------- 1 | /* Please note: This snippet is not executable - it's not part of an overall React project. */ 2 | 3 | import { useState } from 'react'; 4 | 5 | function App() { 6 | const [outputText, setOutputText] = useState(false); 7 | 8 | function updateTextHandler() { 9 | setOutputText('Text was changed!'); 10 | } 11 | 12 | return ( 13 | <> 14 | 15 |

{outputText}

16 | 17 | ); 18 | } 19 | 20 | export default App; -------------------------------------------------------------------------------- /code-snippets/snippet-1/vanilla-javascript/app.js: -------------------------------------------------------------------------------- 1 | const buttonElement = document.querySelector('button'); 2 | const paragraphElement = document.querySelector('p'); 3 | 4 | function updateTextHandler() { 5 | paragraphElement.textContent = 'Text was changed!'; 6 | } 7 | 8 | buttonElement.addEventListener('click', updateTextHandler); -------------------------------------------------------------------------------- /code-snippets/snippet-1/vanilla-javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | Vanilla JavaScript 8 | 9 | 10 | 11 | 12 |

Initial text

13 | 14 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/.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 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/README.md: -------------------------------------------------------------------------------- 1 | # Getting Started with Create React App 2 | 3 | This project was bootstrapped with [Create React App](https://github.com/facebook/create-react-app). 4 | 5 | ## Available Scripts 6 | 7 | In the project directory, you can run: 8 | 9 | ### `npm start` 10 | 11 | Runs the app in the development mode.\ 12 | Open [http://localhost:3000](http://localhost:3000) to view it in the browser. 13 | 14 | The page will reload if you make edits.\ 15 | You will also see any lint errors in the console. 16 | 17 | ### `npm test` 18 | 19 | Launches the test runner in the interactive watch mode.\ 20 | See the section about [running tests](https://facebook.github.io/create-react-app/docs/running-tests) for more information. 21 | 22 | ### `npm run build` 23 | 24 | Builds the app for production to the `build` folder.\ 25 | It correctly bundles React in production mode and optimizes the build for the best performance. 26 | 27 | The build is minified and the filenames include the hashes.\ 28 | Your app is ready to be deployed! 29 | 30 | See the section about [deployment](https://facebook.github.io/create-react-app/docs/deployment) for more information. 31 | 32 | ### `npm run eject` 33 | 34 | **Note: this is a one-way operation. Once you `eject`, you can’t go back!** 35 | 36 | If you aren’t satisfied with the build tool and configuration choices, you can `eject` at any time. This command will remove the single build dependency from your project. 37 | 38 | Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except `eject` will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own. 39 | 40 | You don’t have to ever use `eject`. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it. 41 | 42 | ## Learn More 43 | 44 | You can learn more in the [Create React App documentation](https://facebook.github.io/create-react-app/docs/getting-started). 45 | 46 | To learn React, check out the [React documentation](https://reactjs.org/). 47 | 48 | ### Code Splitting 49 | 50 | This section has moved here: [https://facebook.github.io/create-react-app/docs/code-splitting](https://facebook.github.io/create-react-app/docs/code-splitting) 51 | 52 | ### Analyzing the Bundle Size 53 | 54 | This section has moved here: [https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size](https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size) 55 | 56 | ### Making a Progressive Web App 57 | 58 | This section has moved here: [https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app](https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app) 59 | 60 | ### Advanced Configuration 61 | 62 | This section has moved here: [https://facebook.github.io/create-react-app/docs/advanced-configuration](https://facebook.github.io/create-react-app/docs/advanced-configuration) 63 | 64 | ### Deployment 65 | 66 | This section has moved here: [https://facebook.github.io/create-react-app/docs/deployment](https://facebook.github.io/create-react-app/docs/deployment) 67 | 68 | ### `npm run build` fails to minify 69 | 70 | This section has moved here: [https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify](https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify) 71 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "reactjs", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.14.1", 7 | "@testing-library/react": "^11.2.7", 8 | "@testing-library/user-event": "^12.8.3", 9 | "react": "^17.0.2", 10 | "react-dom": "^17.0.2", 11 | "react-scripts": "4.0.3", 12 | "web-vitals": "^1.1.2" 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 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mschwarzmueller/book-react-key-concepts/6ea666cf63d830baa41b6d737266ee47b027a7a8/examples/example-1/reactjs/public/favicon.ico -------------------------------------------------------------------------------- /examples/example-1/reactjs/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 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/public/logo192.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mschwarzmueller/book-react-key-concepts/6ea666cf63d830baa41b6d737266ee47b027a7a8/examples/example-1/reactjs/public/logo192.png -------------------------------------------------------------------------------- /examples/example-1/reactjs/public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/mschwarzmueller/book-react-key-concepts/6ea666cf63d830baa41b6d737266ee47b027a7a8/examples/example-1/reactjs/public/logo512.png -------------------------------------------------------------------------------- /examples/example-1/reactjs/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 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/public/robots.txt: -------------------------------------------------------------------------------- 1 | # https://www.robotstxt.org/robotstxt.html 2 | User-agent: * 3 | Disallow: 4 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/src/App.js: -------------------------------------------------------------------------------- 1 | import { useState } from 'react'; 2 | 3 | function validateEmail(enteredEmail) { 4 | // In reality, we might be sending the entered email address to a backend API to check if a user with that email exists already 5 | // Here, this is faked with help of a promise wrapper around some dummy validation logic 6 | 7 | const promise = new Promise(function (resolve, reject) { 8 | if (enteredEmail === 'test@test.com') { 9 | reject(new Error('Email exists already')); 10 | } else { 11 | resolve(); 12 | } 13 | }); 14 | 15 | return promise; 16 | } 17 | 18 | function validatePassword(enteredPassword) { 19 | if (enteredPassword.trim().length < 6) { 20 | throw new Error('Invalid password - must be at least 6 characters long.'); 21 | } 22 | } 23 | 24 | function App() { 25 | const [emailIsValid, setEmailIsValid] = useState(true); 26 | const [passwordIsValid, setPasswordIsValid] = useState(true); 27 | const [modalData, setModalData] = useState(null); 28 | 29 | async function validateInputHandler(inputType, event) { 30 | const enteredValue = event.target.value; 31 | 32 | let validationFn = validateEmail; 33 | if (inputType === 'password') { 34 | validationFn = validatePassword; 35 | } 36 | 37 | let isValid = true; 38 | 39 | try { 40 | await validationFn(enteredValue); 41 | } catch (error) { 42 | isValid = false; 43 | } 44 | 45 | if (inputType === 'email') { 46 | setEmailIsValid(isValid); 47 | } else { 48 | setPasswordIsValid(isValid); 49 | } 50 | } 51 | 52 | function submitFormHandler(event) { 53 | event.preventDefault(); 54 | 55 | let title = 'An error occurred!'; 56 | let message = 'Invalid input values - please check your entered values.'; 57 | 58 | if (emailIsValid && passwordIsValid) { 59 | title = 'Success!'; 60 | message = 'User created successfully!'; 61 | } 62 | 63 | setModalData({ 64 | title: title, 65 | message: message, 66 | }); 67 | } 68 | 69 | function closeModal() { 70 | setModalData(null); 71 | } 72 | 73 | return ( 74 | <> 75 | {modalData &&
} 76 | {modalData && ( 77 | 88 | )} 89 |
90 |

Create a New Account

91 |
92 |
93 |
94 |
95 | 96 | 101 | {!emailIsValid &&

This email is already taken!

} 102 |
103 |
104 | 105 | 110 | {!passwordIsValid && ( 111 |

Password must be at least 6 characters long!

112 | )} 113 |
114 | 115 |
116 |
117 | 124 | 125 | ); 126 | } 127 | 128 | export default App; 129 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/src/index.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | font-family: 'Roboto', sans-serif; 7 | } 8 | 9 | body { 10 | margin: 0; 11 | background-color: #f6f5fb; 12 | color: #262527; 13 | } 14 | 15 | header { 16 | margin-top: 3rem; 17 | height: 5rem; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | color: #2d0d58; 22 | } 23 | 24 | footer { 25 | text-align: center; 26 | color: #6a666e; 27 | } 28 | 29 | button { 30 | font: inherit; 31 | padding: 0.5rem 1.5rem; 32 | border-radius: 4px; 33 | background-color: #d36f11; 34 | border: 1px solid #d36f11; 35 | color: white; 36 | cursor: pointer; 37 | } 38 | 39 | button:hover, 40 | button:active { 41 | background-color: #d39611; 42 | border-color: #d39611; 43 | } 44 | 45 | main form { 46 | max-width: 30rem; 47 | margin: 2rem auto; 48 | padding: 2rem; 49 | border-radius: 6px; 50 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); 51 | background-color: #2d0d58; 52 | color: white; 53 | text-align: center; 54 | } 55 | 56 | label { 57 | display: block; 58 | font-weight: bold; 59 | margin-bottom: 0.5rem; 60 | } 61 | 62 | input { 63 | width: 80%; 64 | font: inherit; 65 | padding: 0.25rem; 66 | background-color: #f3edfb; 67 | border: none; 68 | border-radius: 4px; 69 | } 70 | 71 | input:focus { 72 | background-color: #dcc3fd; 73 | } 74 | 75 | .form-control { 76 | margin-bottom: 1rem; 77 | } 78 | 79 | .backdrop { 80 | position: fixed; 81 | top: 0; 82 | left: 0; 83 | width: 100%; 84 | height: 100vh; 85 | z-index: 1; 86 | background-color: rgba(0, 0, 0, 0.7); 87 | } 88 | 89 | .modal { 90 | position: fixed; 91 | top: 20vh; 92 | left: calc(50% - 20rem); 93 | width: 40rem; 94 | z-index: 2; 95 | border-radius: 6px; 96 | overflow: hidden; 97 | background-color: white; 98 | } 99 | 100 | .modal header { 101 | margin: 0; 102 | background-color: #2d0d58; 103 | color: white; 104 | padding: 1rem; 105 | justify-content: flex-start; 106 | } 107 | 108 | .modal header h2 { 109 | margin: 0; 110 | } 111 | 112 | .modal section { 113 | padding: 1rem; 114 | } 115 | 116 | .modal__actions { 117 | text-align: right; 118 | } 119 | -------------------------------------------------------------------------------- /examples/example-1/reactjs/src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom'; 3 | 4 | import './index.css'; 5 | import App from './App'; 6 | 7 | ReactDOM.render( 8 | 9 | 10 | , 11 | document.getElementById('root') 12 | ); 13 | -------------------------------------------------------------------------------- /examples/example-1/vanilla-javascript/app.js: -------------------------------------------------------------------------------- 1 | /* 2 | (c) Maximilian Schwarzmüller 3 | 4 | This example, like all snippets and examples in this repository, is used in and referenced by my React.js book [BOOK TITLE AND LINK]. 5 | All examples can be used and edited but no warranty or guarantee of correctness is provided by me. See the README.MD and LICENSE.MD files for more details! 6 | */ 7 | 8 | const emailInputElement = document.getElementById('email'); 9 | const passwordInputElement = document.getElementById('password'); 10 | const signupFormElement = document.querySelector('form'); 11 | 12 | let emailIsValid = false; 13 | let passwordIsValid = false; 14 | 15 | function validateEmail(enteredEmail) { 16 | // In reality, we might be sending the entered email address to a backend API to check if a user with that email exists already 17 | // Here, this is faked with help of a promise wrapper around some dummy validation logic 18 | 19 | const promise = new Promise(function (resolve, reject) { 20 | if (enteredEmail === 'test@test.com') { 21 | reject(new Error('Email exists already')); 22 | } else { 23 | resolve(); 24 | } 25 | }); 26 | 27 | return promise; 28 | } 29 | 30 | function validatePassword(enteredPassword) { 31 | if (enteredPassword.trim().length < 6) { 32 | throw new Error('Invalid password - must be at least 6 characters long.'); 33 | } 34 | } 35 | 36 | async function validateInputHandler(inputType, event) { 37 | const targetElement = event.target; 38 | const enteredValue = targetElement.value; 39 | 40 | let validationFn = validateEmail; 41 | if (inputType === 'password') { 42 | validationFn = validatePassword; 43 | } 44 | 45 | const errorElement = document.getElementById(inputType + '-error'); 46 | if (errorElement) { 47 | errorElement.remove(); 48 | } 49 | 50 | let isValid = true; 51 | 52 | try { 53 | await validationFn(enteredValue); 54 | } catch (error) { 55 | const errorElement = document.createElement('p'); 56 | errorElement.id = inputType + '-error'; 57 | errorElement.textContent = error.message; 58 | targetElement.parentElement.append(errorElement); 59 | isValid = false; 60 | } 61 | 62 | if (inputType === 'email') { 63 | emailIsValid = isValid; 64 | } else { 65 | passwordIsValid = isValid; 66 | } 67 | } 68 | 69 | function submitFormHandler(event) { 70 | event.preventDefault(); 71 | 72 | let title = 'An error occurred!'; 73 | let message = 'Invalid input values - please check your entered values.'; 74 | 75 | if (emailIsValid && passwordIsValid) { 76 | title = 'Success!'; 77 | message = 'User created successfully!'; 78 | } 79 | 80 | openModal(title, message); 81 | } 82 | 83 | function openModal(title, message) { 84 | const backdropElement = document.createElement('div'); 85 | backdropElement.className = 'backdrop'; 86 | 87 | const modalElement = document.createElement('aside'); 88 | modalElement.className = 'modal'; 89 | modalElement.innerHTML = ` 90 |
91 |

${title}

92 |
93 |
94 |

${message}

95 |
96 | 99 | `; 100 | const confirmButtonElement = modalElement.querySelector('button'); 101 | 102 | backdropElement.addEventListener('click', closeModal); 103 | confirmButtonElement.addEventListener('click', closeModal); 104 | 105 | document.body.append(backdropElement); 106 | document.body.append(modalElement); 107 | } 108 | 109 | function closeModal() { 110 | const modalElement = document.querySelector('.modal'); 111 | const backdropElement = document.querySelector('.backdrop'); 112 | 113 | modalElement.remove(); 114 | backdropElement.remove(); 115 | } 116 | 117 | emailInputElement.addEventListener( 118 | 'blur', 119 | validateInputHandler.bind(null, 'email') 120 | ); 121 | passwordInputElement.addEventListener( 122 | 'blur', 123 | validateInputHandler.bind(null, 'password') 124 | ); 125 | 126 | signupFormElement.addEventListener('submit', submitFormHandler); 127 | -------------------------------------------------------------------------------- /examples/example-1/vanilla-javascript/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Vanilla JavaScript Example 13 | 14 | 15 | 16 | 17 |
18 |

Create a New Account

19 |
20 |
21 |
22 |
23 | 24 | 25 |
26 |
27 | 28 | 29 |
30 | 31 |
32 |
33 | 37 | 38 | 39 | -------------------------------------------------------------------------------- /examples/example-1/vanilla-javascript/styles.css: -------------------------------------------------------------------------------- 1 | * { 2 | box-sizing: border-box; 3 | } 4 | 5 | html { 6 | font-family: 'Roboto', sans-serif; 7 | } 8 | 9 | body { 10 | margin: 0; 11 | background-color: #f6f5fb; 12 | color: #262527; 13 | } 14 | 15 | header { 16 | margin-top: 3rem; 17 | height: 5rem; 18 | display: flex; 19 | justify-content: center; 20 | align-items: center; 21 | color: #2d0d58; 22 | } 23 | 24 | footer { 25 | text-align: center; 26 | color: #6a666e; 27 | } 28 | 29 | button { 30 | font: inherit; 31 | padding: 0.5rem 1.5rem; 32 | border-radius: 4px; 33 | background-color: #d36f11; 34 | border: 1px solid #d36f11; 35 | color: white; 36 | cursor: pointer; 37 | } 38 | 39 | button:hover, 40 | button:active { 41 | background-color: #d39611; 42 | border-color: #d39611; 43 | } 44 | 45 | main form { 46 | max-width: 30rem; 47 | margin: 2rem auto; 48 | padding: 2rem; 49 | border-radius: 6px; 50 | box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2); 51 | background-color: #2d0d58; 52 | color: white; 53 | text-align: center; 54 | } 55 | 56 | label { 57 | display: block; 58 | font-weight: bold; 59 | margin-bottom: 0.5rem; 60 | } 61 | 62 | input { 63 | width: 80%; 64 | font: inherit; 65 | padding: 0.25rem; 66 | background-color: #f3edfb; 67 | border: none; 68 | border-radius: 4px; 69 | } 70 | 71 | input:focus { 72 | background-color: #dcc3fd; 73 | } 74 | 75 | .form-control { 76 | margin-bottom: 1rem; 77 | } 78 | 79 | .backdrop { 80 | position: fixed; 81 | top: 0; 82 | left: 0; 83 | width: 100%; 84 | height: 100vh; 85 | z-index: 1; 86 | background-color: rgba(0, 0, 0, 0.7); 87 | } 88 | 89 | .modal { 90 | position: fixed; 91 | top: 20vh; 92 | left: calc(50% - 20rem); 93 | width: 40rem; 94 | z-index: 2; 95 | border-radius: 6px; 96 | overflow: hidden; 97 | background-color: white; 98 | } 99 | 100 | .modal header { 101 | margin: 0; 102 | background-color: #2d0d58; 103 | color: white; 104 | padding: 1rem; 105 | justify-content: flex-start; 106 | } 107 | 108 | .modal header h2 { 109 | margin: 0; 110 | } 111 | 112 | .modal section { 113 | padding: 1rem; 114 | } 115 | 116 | .modal__actions { 117 | text-align: right; 118 | } 119 | -------------------------------------------------------------------------------- /exercises/questions-answers.md: -------------------------------------------------------------------------------- 1 | # 1. What is React? 2 | 3 | React (or React.js) is a JavaScript library that simplifies building (complex) user interfaces. It's exposed through the [react](https://www.npmjs.com/package/react) package, and combined with the [react-dom](https://www.npmjs.com/package/react-dom) package, you can use it to build advanced, highly interactive an reactive web user interfaces. 4 | 5 | React is platform-agnostic though, you can also use it with other platforms - most notably, you can build native mobile apps with React and the [react-native](https://www.npmjs.com/package/react-native) package. 6 | 7 | # 2. Which advantage does React offer over vanilla JavaScript projects? 8 | 9 | With "vanilla JavaScript" (i.e. JavaScript without any extra libraries or frameworks), you have to write all code instructions yourself. 10 | 11 | For basic web apps and user interfaces, this is typically no problem but for more advanced use-cases, it can quickly become very cumbersome and error prone to use just vanilla JavaScript (also see the next question: *Imperative vs declarative code*). 12 | 13 | React simplifies the creation of (complex) web user interfaces by generating and executing DOM-manipulating instructions "under the hood", on your behalf. As a developer, you can focus on describing the desired target UI state(s) and the core business logic. You don't have to control all UI updates and changes manually. 14 | 15 | # 3. What’s the difference between imperative and declarative code? 16 | 17 | With *imperative code*, you write down all instructions step by step. If you want to listen to a button click and then change some text on the screen, you have to add the code that sets up the event listener and then also the code that selects the to-be-changed element as well as the code that does set a new text for that element. 18 | For non-trivial user interfaces, this can lead to lots of overhead work and possible error sources. 19 | 20 | When writing code *declaratively*, you don't write down all steps that lead to a certain result. Instead, as a developer, you describe the target (UI) state(s) and let some library (in this case: React) figure out how to get there. This allows you to skip the overhead work of selecting DOM elements and changing them manually and focus on your core business logic instead. 21 | 22 | # 4. What is a Single-Page-Application (SPA)? 23 | 24 | A SPA is a web app that is technically served on one page (one HTML document) only. That page loads some JavaScript code (typically combined with some library like React) that takes care about updating the page content based on different user actions. 25 | To the user, it might seem like they are navigating different pages but technically, it's the same page's DOM getting updated behind the scenes. 26 | 27 | You can even listen to changes in the page path to update the DOM based on path changes - this increases the "multi page feeling". When working with React, the [react-router](https://www.npmjs.com/package/react-router) package is the most popular package for listening to such path changes and updating the UI based on any changes. 28 | 29 | # 5. How can you create new React projects and why do you need such a more complex project setup? 30 | 31 | New React projects can be created in different ways but one of the most popular and easiest ways of setting up a new React project is to use the [create-react-app tool](https://reactjs.org/docs/create-a-new-react-app.html). 32 | 33 | This is a tool which you can use to scaffold new React projects which already have all the packages (like `react` and `react-dom`) that are needed to build web projects with React. In addition, those projects also come with some code transformation tools and a workflow that enables the usage of special features like *JSX code* (markup code for React apps). 34 | 35 | New projects can be created with that tool by running `npx create-react-app project-name`. 36 | 37 | These projects also provide a development server which can be started via `npm start` to preview the React web app locally and get live updates as you make code changes. --------------------------------------------------------------------------------