,
7 | document.getElementById('root')
8 | );
--------------------------------------------------------------------------------
/.canvas:
--------------------------------------------------------------------------------
1 | ---
2 | :lessons:
3 | - :id: 250558
4 | :course_id: 6667
5 | :canvas_url: https://learning.flatironschool.com/courses/6667/pages/react-forms-submit
6 | :type: page
7 | - :id: 296593
8 | :course_id: 7553
9 | :canvas_url: https://learning.flatironschool.com/courses/7553/pages/react-forms-submit
10 | :type: page
11 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/src/components/Form.js:
--------------------------------------------------------------------------------
1 | import React, { useState } from "react";
2 |
3 | function Form(props) {
4 | const [firstName, setFirstName] = useState("Sylvia");
5 | const [lastName, setLastName] = useState("Woods");
6 |
7 | function handleFirstNameChange(event) {
8 | setFirstName(event.target.value);
9 | }
10 |
11 | function handleLastNameChange(event) {
12 | setLastName(event.target.value);
13 | }
14 |
15 | return (
16 |
21 | );
22 | }
23 |
24 | export default Form;
25 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "react-hooks-forms-submit",
3 | "version": "0.1.0",
4 | "private": true,
5 | "dependencies": {
6 | "react": "^17.0.1",
7 | "react-dom": "^17.0.1",
8 | "react-scripts": "^5.0.1"
9 | },
10 | "scripts": {
11 | "start": "react-scripts start",
12 | "test": "jest"
13 | },
14 | "eslintConfig": {
15 | "extends": [
16 | "react-app",
17 | "react-app/jest"
18 | ]
19 | },
20 | "browserslist": {
21 | "production": [
22 | ">0.2%",
23 | "not dead",
24 | "not op_mini all"
25 | ],
26 | "development": [
27 | "last 1 chrome version",
28 | "last 1 firefox version",
29 | "last 1 safari version"
30 | ]
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/.github/workflows/canvas-sync-ruby-update.yml:
--------------------------------------------------------------------------------
1 | name: Sync with Canvas Ruby v2.7
2 |
3 | on:
4 | push:
5 | branches: [master, main]
6 | paths:
7 | - 'README.md'
8 |
9 | jobs:
10 | sync:
11 | name: Sync with Canvas
12 |
13 | runs-on: ubuntu-latest
14 |
15 | steps:
16 | - uses: actions/checkout@v2
17 |
18 | - name: Set up Ruby
19 | uses: ruby/setup-ruby@v1
20 | with:
21 | ruby-version: 2.7
22 |
23 | - name: Install github-to-canvas
24 | run: gem install github-to-canvas
25 |
26 | # Secret stored in learn-co-curriculum Settings/Secrets
27 | - name: Sync from .canvas file
28 | run: github-to-canvas -a -lr
29 | env:
30 | CANVAS_API_KEY: ${{ secrets.CANVAS_API_KEY }}
31 | CANVAS_API_PATH: ${{ secrets.CANVAS_API_PATH }}
32 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.gitignore.io/api/node
2 |
3 | ### Node ###
4 | # Logs
5 | logs
6 | *.log
7 | npm-debug.log*
8 |
9 | # Runtime data
10 | pids
11 | *.pid
12 | *.seed
13 |
14 | # Directory for instrumented libs generated by jscoverage/JSCover
15 | lib-cov
16 |
17 | # Coverage directory used by tools like istanbul
18 | coverage
19 |
20 | # nyc test coverage
21 | .nyc_output
22 |
23 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files)
24 | .grunt
25 |
26 | # node-waf configuration
27 | .lock-wscript
28 |
29 | # Compiled binary addons (http://nodejs.org/api/addons.html)
30 | build/Release
31 |
32 | # Dependency directories
33 | node_modules
34 | jspm_packages
35 |
36 | # Optional npm cache directory
37 | .npm
38 |
39 | # Optional REPL history
40 | .node_repl_history
41 |
42 | # Learn-specific .results.json
43 | .results.json
44 |
45 | # Yarn Lockfile
46 | yarn.lock
47 |
48 | # DS_Store
49 | .DS_Store
50 |
51 | .eslintcache
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | # Learn.co Educational Content License
2 |
3 | Copyright (c) 2016 Flatiron School, Inc
4 |
5 | The Flatiron School, Inc. owns this Educational Content. However, the Flatiron School supports the development and availability of educational materials in the public domain. Therefore, the Flatiron School grants Users of the Flatiron Educational Content set forth in this repository certain rights to reuse, build upon and share such Educational Content subject to the terms of the Educational Content License set forth [here](http://learn.co/content-license) (http://learn.co/content-license). You must read carefully the terms and conditions contained in the Educational Content License as such terms govern access to and use of the Educational Content.
6 |
7 | Flatiron School is willing to allow you access to and use of the Educational Content only on the condition that you accept all of the terms and conditions contained in the Educational Content License set forth [here](http://learn.co/content-license) (http://learn.co/content-license). By accessing and/or using the Educational Content, you are agreeing to all of the terms and conditions contained in the Educational Content License. If you do not agree to any or all of the terms of the Educational Content License, you are prohibited from accessing, reviewing or using in any way the Educational Content.
8 |
--------------------------------------------------------------------------------
/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 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing to Learn.co Curriculum
2 |
3 | We're really excited that you're about to contribute to the [open curriculum](https://learn.co/content-license) on [Learn.co](https://learn.co). If this is your first time contributing, please continue reading to learn how to make the most meaningful and useful impact possible.
4 |
5 | ## Raising an Issue to Encourage a Contribution
6 |
7 | If you notice a problem with the curriculum that you believe needs improvement
8 | but you're unable to make the change yourself, you should raise a Github issue
9 | containing a clear description of the problem. Include relevant snippets of
10 | the content and/or screenshots if applicable. Curriculum owners regularly review
11 | issue lists and your issue will be prioritized and addressed as appropriate.
12 |
13 | ## Submitting a Pull Request to Suggest an Improvement
14 |
15 | If you see an opportunity for improvement and can make the change yourself go
16 | ahead and use a typical git workflow to make it happen:
17 |
18 | * Fork this curriculum repository
19 | * Make the change on your fork, with descriptive commits in the standard format
20 | * Open a Pull Request against this repo
21 |
22 | A curriculum owner will review your change and approve or comment on it in due
23 | course.
24 |
25 | # Why Contribute?
26 |
27 | Curriculum on Learn is publicly and freely available under Learn's
28 | [Educational Content License](https://learn.co/content-license). By
29 | embracing an open-source contribution model, our goal is for the curriculum
30 | on Learn to become, in time, the best educational content the world has
31 | ever seen.
32 |
33 | We need help from the community of Learners to maintain and improve the
34 | educational content. Everything from fixing typos, to correcting
35 | out-dated information, to improving exposition, to adding better examples,
36 | to fixing tests—all contributions to making the curriculum more effective are
37 | welcome.
38 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # React Forms Submit
2 |
3 | ## Learning Goals
4 |
5 | - Handle a form's submit event in React
6 | - Use controlled inputs to validate values
7 |
8 | ## Introduction
9 |
10 | In this lesson, we'll discuss how to handle form submission in React.
11 |
12 | If you want to code along there is starter code in the `src` folder. Make sure
13 | to run `npm install && npm start` to see the code in the browser.
14 |
15 | ## Submitting a Controlled Form
16 |
17 | Now that we've learned how to control a form with state, we want to set up a way
18 | to submit our form. For this, we add the `onSubmit` event listener to our `form`
19 | element:
20 |
21 | ```jsx
22 | // src/components/Form.js
23 | return (
24 |
29 | );
30 | ```
31 |
32 | Now, whenever the form is submitted (by pressing the Enter or Return key in an
33 | input field, or by clicking a Submit button), the `handleSubmit` callback
34 | function will be called. We don't have the `handleSubmit` function yet, so let's
35 | write it out:
36 |
37 | ```jsx
38 | function handleSubmit(event) {
39 | event.preventDefault();
40 | const formData = {
41 | firstName: firstName,
42 | lastName: lastName,
43 | };
44 | props.sendFormDataSomewhere(formData);
45 | setFirstName("");
46 | setLastName("");
47 | }
48 | ```
49 |
50 | Let's look at each line of code in this function:
51 |
52 | - `event.preventDefault()`: The default behavior of a form is to
53 | [try and submit the form data based on a defined action][], which effectively
54 | causes the browser to refresh the page. We didn't (and don't need to) define
55 | an action. The result, however, is that the form makes a new request to the
56 | current page, causing a refresh. By using `event.preventDefault()`, we stop
57 | this behavior from happening.
58 |
59 | [try and submit the form data based on a defined action]: https://www.w3schools.com/html/html_forms.asp
60 |
61 | - `const formData = { firstName: firstName, lastName: lastName }`: Here, we are
62 | putting together the current form data into an object using the values stored
63 | in state.
64 | - `props.sendFormDataSomewhere(formData)`: A form, when submitted, should send
65 | the form data somewhere. As mentioned a moment ago, the traditional HTML way
66 | was to send data to a server or another page using the `action` attribute. In
67 | React, we handle requests with asynchronous JavaScript. We won't go into the
68 | details of how this works just yet, but we can think of
69 | `sendFormDataSomewhere()` as the code that handles sending our data off. This
70 | function might be defined in the same form component, or can be passed down as
71 | a prop.
72 | - `setFirstName("")`: if we want to clear the input fields, all we need to do is
73 | set state! In a traditional JavaScript form, you might do something like
74 | `event.target.reset()` to clear out the form fields. Here, because we are
75 | using controlled inputs, setting state to an empty string clears out the
76 | values from the input fields once the data has been submitted.
77 |
78 | You can contrast this to handling an _uncontrolled_ form being submitted, in
79 | which case you would need to access the input fields from the DOM instead
80 | of accessing the values from state:
81 |
82 | ```jsx
83 | function handleSubmit(event) {
84 | event.preventDefault();
85 | // in an uncontrolled form, you need to access the input fields from the DOM
86 | const formData = {
87 | firstName: e.target[0].value,
88 | lastName: e.target[1].value,
89 | };
90 | props.sendFormDataSomewhere(formData);
91 | }
92 | ```
93 |
94 | Since we don't have a server to send our data to, let's remove our
95 | `sendFormDataSomewhere()` function. Instead, we'll demonstrate submission by
96 | modifying our `Form` component to access submitted values from state and list
97 | them in the DOM:
98 |
99 | ```jsx
100 | import React, { useState } from "react";
101 |
102 | function Form() {
103 | const [firstName, setFirstName] = useState("Sylvia");
104 | const [lastName, setLastName] = useState("Woods");
105 | const [submittedData, setSubmittedData] = useState([]);
106 |
107 | function handleFirstNameChange(event) {
108 | setFirstName(event.target.value);
109 | }
110 |
111 | function handleLastNameChange(event) {
112 | setLastName(event.target.value);
113 | }
114 |
115 | function handleSubmit(event) {
116 | event.preventDefault();
117 | const formData = { firstName: firstName, lastName: lastName };
118 | const dataArray = [...submittedData, formData];
119 | setSubmittedData(dataArray);
120 | setFirstName("");
121 | setLastName("");
122 | }
123 |
124 | const listOfSubmissions = submittedData.map((data, index) => {
125 | return (
126 |
127 | {data.firstName} {data.lastName}
128 |
129 | );
130 | });
131 |
132 | return (
133 |
134 |
139 |
Submissions
140 | {listOfSubmissions}
141 |
142 | );
143 | }
144 |
145 | export default Form;
146 | ```
147 |
148 | The above component will render previous form submissions on the page! We have
149 | a fully functioning controlled form.
150 |
151 | ## Validating Inputs
152 |
153 | One benefit we get from having our form's input values held in state is an easy
154 | way to perform validations when the form is submitted. For example, let's say we
155 | want to require that a user enter some data into our form fields before they
156 | can submit the form successfully.
157 |
158 | In our `handleSubmit` function, we can add some validation logic to check if the
159 | form inputs have the required data, and hold some error messages in state:
160 |
161 | ```jsx
162 | // add state for holding error messages
163 | const [errors, setErrors] = useState([]);
164 |
165 | function handleSubmit(event) {
166 | event.preventDefault();
167 | // first name is required
168 | if (firstName.length > 0) {
169 | const formData = { firstName: firstName, lastName: lastName };
170 | const dataArray = [...submittedData, formData];
171 | setSubmittedData(dataArray);
172 | setFirstName("");
173 | setLastName("");
174 | setErrors([]);
175 | } else {
176 | setErrors(["First name is required!"]);
177 | }
178 | }
179 | ```
180 |
181 | Then, we can display an error message to our user in the JSX:
182 |
183 | ```jsx
184 | return (
185 |
202 | );
203 | ```
204 |
205 | ## Conclusion
206 |
207 | By setting up our form components using controlled inputs, we give React state
208 | control over the data being displayed in the DOM. As a benefit of having the
209 | form data in state, we can more easily access it once a form is submitted and
210 | either pass it along to another component or use it to make a fetch request. We
211 | can also more easily perform some validation logic when the form data is
212 | submitted.
213 |
214 | ## Resources
215 |
216 | - [React Forms](https://reactjs.org/docs/forms.html)
217 |
--------------------------------------------------------------------------------