├── .gitignore ├── README.md ├── package-lock.json ├── package.json ├── public ├── favicon.ico ├── index.html ├── logo192.png ├── logo512.png ├── manifest.json └── robots.txt └── src ├── App.js ├── components ├── Billing.js └── Form.js ├── index.css └── index.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 | # "React Form Change Handler" 2 | 3 | --- 4 | 5 | ### Author Links 6 | 7 | 👋 Hello, I'm Dave Gray. 8 | 9 | ✅ [Check out my YouTube Channel with all of my tutorials](https://www.youtube.com/DaveGrayTeachesCode). 10 | 11 | 🚩 [Subscribe to my channel](https://bit.ly/3nGHmNn) 12 | 13 | ☕ [Buy Me A Coffee](https://buymeacoffee.com/DaveGray) 14 | 15 | 🚀 Follow Me: 16 | 17 | - [Twitter](https://twitter.com/yesdavidgray) 18 | - [LinkedIn](https://www.linkedin.com/in/davidagray/) 19 | - [Blog](https://yesdavidgray.com) 20 | - [Reddit](https://www.reddit.com/user/DaveOnEleven) 21 | 22 | --- 23 | 24 | ### Description 25 | 26 | 📺 [YouTube Video](https://youtu.be/FGM6FQmdX8I) for this repository. 27 | 28 | --- 29 | 30 | ### 💻 Source Code 31 | 32 | - 🔗 [React Form Change Handler Tutorial - Completed Source Code](https://github.com/gitdagray/react-change-handler) 33 | 34 | --- 35 | 36 | ### 🎓 Academic Honesty 37 | 38 | **DO NOT COPY FOR AN ASSIGNMENT** - Avoid plagiargism and adhere to the spirit of this [Academic Honesty Policy](https://www.freecodecamp.org/news/academic-honesty-policy/). 39 | 40 | --- 41 | 42 | ### ⚙ VS Code Extensions I Use: 43 | 44 | - 🔗 [ES7 React JS Snippets Extension](https://marketplace.visualstudio.com/items?itemName=dsznajder.es7-react-js-snippets) 45 | - 🔗 [vscode-icons VS Code Extension](https://marketplace.visualstudio.com/items?itemName=vscode-icons-team.vscode-icons) 46 | - 🔗 [Github Themes VS Code Extension](https://marketplace.visualstudio.com/items?itemName=GitHub.github-vscode-theme) -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "react-change-handler", 3 | "version": "0.1.0", 4 | "private": true, 5 | "dependencies": { 6 | "@testing-library/jest-dom": "^5.16.5", 7 | "@testing-library/react": "^13.4.0", 8 | "@testing-library/user-event": "^13.5.0", 9 | "react": "^18.2.0", 10 | "react-dom": "^18.2.0", 11 | "react-scripts": "5.0.1", 12 | "web-vitals": "^2.1.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 | } -------------------------------------------------------------------------------- /public/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitdagray/react-change-handler/306d3bffa7041855ed6f6d46d19ee38535afda40/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/gitdagray/react-change-handler/306d3bffa7041855ed6f6d46d19ee38535afda40/public/logo192.png -------------------------------------------------------------------------------- /public/logo512.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/gitdagray/react-change-handler/306d3bffa7041855ed6f6d46d19ee38535afda40/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 Form from "./components/Form" 2 | 3 | function App() { 4 | 5 | return
6 | 7 | } 8 | 9 | export default App; 10 | -------------------------------------------------------------------------------- /src/components/Billing.js: -------------------------------------------------------------------------------- 1 | const Billing = ({ data, handleChange }) => { 2 | 3 | const content = ( 4 |
5 |
6 |
7 | 8 | 17 |
18 |
19 | 20 | 29 |
30 |
31 | 32 | 33 | 42 | 43 | 44 | 53 | 54 | 55 | 64 | 65 | 66 | 124 | 125 | 126 | 136 |
137 | ) 138 | 139 | return content 140 | } 141 | export default Billing -------------------------------------------------------------------------------- /src/components/Form.js: -------------------------------------------------------------------------------- 1 | import Billing from "./Billing" 2 | import { useState } from "react" 3 | 4 | const Form = () => { 5 | const [data, setData] = useState({ 6 | billFirstName: "", 7 | billLastName: "", 8 | billAddress1: "", 9 | billAddress2: "", 10 | billCity: "", 11 | billState: "", 12 | billZipCode: "" 13 | }) 14 | 15 | const handleSubmit = e => { 16 | e.preventDefault() 17 | console.log(JSON.stringify(data)) 18 | } 19 | 20 | const handleChange = e => { 21 | const type = e.target.type 22 | 23 | const name = e.target.name 24 | 25 | const value = type === "checkbox" 26 | ? e.target.checked 27 | : e.target.value 28 | 29 | setData(prevData => ({ 30 | ...prevData, 31 | [name]: value 32 | })) 33 | } 34 | 35 | const { billAddress2, ...otherProps } = data 36 | 37 | const canSave = [...Object.values(otherProps)].every(Boolean) 38 | 39 | const content = ( 40 | 41 | 42 |

Billing Info

43 | 44 | 45 | 46 | 47 | 48 | 49 | ) 50 | 51 | return content 52 | } 53 | export default Form -------------------------------------------------------------------------------- /src/index.css: -------------------------------------------------------------------------------- 1 | @import url('https://fonts.googleapis.com/css2?family=Nunito&display=swap'); 2 | 3 | * { 4 | margin: 0; 5 | padding: 0; 6 | box-sizing: border-box; 7 | } 8 | 9 | /* || UTILITY CLASSES */ 10 | 11 | .remove-button { 12 | display: none; 13 | } 14 | 15 | .offscreen { 16 | position: absolute; 17 | left: -10000px; 18 | } 19 | 20 | .split-container { 21 | display: flex; 22 | flex-flow: row nowrap; 23 | justify-content: space-evenly; 24 | gap: 1rem; 25 | } 26 | 27 | .flex-col { 28 | display: flex; 29 | flex-flow: column nowrap; 30 | gap: 0.35rem; 31 | width: 100%; 32 | } 33 | 34 | input:invalid { 35 | outline: 2px solid red; 36 | background-color: rgb(255, 255, 139); 37 | } 38 | 39 | 40 | /* || GENERAL STYLES */ 41 | 42 | body { 43 | font-family: 'Nunito', sans-serif; 44 | min-height: 100vh; 45 | background: radial-gradient(skyblue, darkblue); 46 | padding: 1rem; 47 | } 48 | 49 | input, 50 | button, 51 | textarea, 52 | select { 53 | font: inherit; 54 | } 55 | 56 | #root { 57 | display: flex; 58 | flex-flow: column nowrap; 59 | gap: 0.5rem; 60 | } 61 | 62 | .progress-container { 63 | width: 100%; 64 | max-width: 600px; 65 | margin: auto; 66 | padding: 1rem; 67 | border: 1px solid #000; 68 | border-radius: 10px; 69 | background-color: whitesmoke; 70 | } 71 | 72 | .barmarker-container { 73 | display: flex; 74 | flex-flow: row nowrap; 75 | justify-content: space-around; 76 | } 77 | 78 | .barmarker { 79 | width: 100%; 80 | display: grid; 81 | place-content: center; 82 | } 83 | 84 | .barmarker:is(:not(:last-child)) { 85 | border-right: 1px solid #000; 86 | } 87 | 88 | .progress { 89 | width: 100%; 90 | height: 24px; 91 | } 92 | 93 | .form { 94 | width: 100%; 95 | max-width: 600px; 96 | margin: auto; 97 | padding: 0.75rem; 98 | border: 1px solid #000; 99 | border-radius: 10px; 100 | background-color: whitesmoke; 101 | } 102 | 103 | .form-header { 104 | display: flex; 105 | flex-flow: row nowrap; 106 | justify-content: space-between; 107 | align-items: center; 108 | } 109 | 110 | .button-container { 111 | display: flex; 112 | flex-flow: row nowrap; 113 | gap: 0.5rem; 114 | } 115 | 116 | .button { 117 | padding: 0.5rem; 118 | border-radius: 10px; 119 | } 120 | 121 | .button:focus { 122 | outline: 2px solid gold; 123 | } 124 | 125 | input, 126 | select { 127 | padding: 0.5rem; 128 | border-radius: 10px; 129 | width: 100%; 130 | } 131 | 132 | input[type="checkbox"] { 133 | width: 16px; 134 | height: 16px; 135 | margin-right: 0.5rem; 136 | } 137 | 138 | ul { 139 | list-style-type: none; 140 | margin-bottom: 1rem; 141 | } -------------------------------------------------------------------------------- /src/index.js: -------------------------------------------------------------------------------- 1 | import React from 'react'; 2 | import ReactDOM from 'react-dom/client'; 3 | import './index.css'; 4 | import App from './App'; 5 | 6 | const root = ReactDOM.createRoot(document.getElementById('root')); 7 | root.render( 8 | 9 | 10 | 11 | ); 12 | --------------------------------------------------------------------------------