Forms
Controlled Components
In HTML, form elements such as <input>
, <textarea>
, and <select>
typically maintain their own state and update it based on user input.
For example, if you type in a text input the value property of the element holds what you typed (controls it).
In React, mutable state is typically kept in the state property of components, and only updated with setState().
We can combine the two by making the React state be the “single source of truth”. Then the React component that renders a form also controls what happens in that form on subsequent user input. An input form element whose value is controlled by React in this way is called a controlled component.
Controlled Function Components
Below is an example of what a controlled component would like like in a function component.
- Delete the current code in
main.js
. - Add the following code to
main.js
- Refresh your browser
- Type some text in the
input
- Notice that this text immediately shows in
state
because we have written set the value and onChange properties to read and write from the parent component's surrounding state. - Update
handleChange
as follows
- Refresh the browser
- Type some text in the
input
- We can now more clearly see we are controlling the value by storing it in the component's state.
- Remove the
toUpperCase()
call - To further understand controlled components: Comment out the implementation of
handleChange
and notice that when you type in the input nothing happens.
- Uncomment the
handleChange
implementation and verify it is working again.
Controlled Class Components
Below is an example of what a controlled component would look like in a class component.
Submitting
Handling the submission of the form using the same concepts we learning previously in the events section.
- Modify the code to prevent the default browser behavior of submitting the form data to the server and instead log the form data to the
console
.
Function Component Example
Class Component Example
Controlling other Types of HTML Form Elements
The following example of a contact us form demonstrates how controlling other HTML form fields such as: <select>
, textarea
, and <input type='checkbox'>
is very similar to how we work with an <input>
.
Function Component Example
Class Component Example
Notice that although these HTML form fields set their value differently:
<textarea>value goes here<textarea>
- The option with
selected
is selected.<select name="department"><option value="">Select...</option><option value="hr">Human Resources</option><option selected value="pr">Public Relations</option><option value="support">Support</option></select> <input type='checkbox' checked=checked>
These have all been standardized to be set using the value property when using React.
Validation
- Validation of forms is something you can do in plain vanilla JavaScript.
- Validating user input is not even discussed in the React documentation.
- There are no specific features in React for validating forms.
- React leaves this to job for external libraries.
- Currently, the most popular React form validation library is Formik.
- Here is a discussion on reddit of whether a Form Library is Necessary in React
To help you decide whether a library would be helpful in your use case, it can be helpful to manually implement form validation at first in your React application.
Below is an example of some basic validation implemented in our Contact Us form.
Validation (with Function Component & Hooks)
- Create the file
styles.css
- Add the following styles:
styles.css
index.html
main.js
Some things to notice in the code above:
- Validation messages are themselves local component state.
- The validations are called when the form is submitted.
- The && operator is used to conditionally display the error messages.
- The && operator is ideal in this case since there is no else case.
Validation (in a Class Component)
main.js
Uncontrolled Components
In most cases, React recommends using controlled components
to implement forms. In a controlled component, form data is handled by a React component. The alternative is uncontrolled components
, where form data is handled by the DOM
itself.
Refs
When writing an uncontrolled component
you use a ref
to get form values from the DOM directly instead of writing an event handler for every state update.
Function Component Example
Setting defaultValue
Try initializing the value property on the input.
- Modify the code to set the
value
property<form onSubmit={handleSubmit}><input+ value="initial value"type="text" ref={input} /><button>Submit</button></form> - Refresh the page
- This warning is displayed and the input will be read-only:Warning: Failed prop type: You provided a `value` prop to a form field without an `onChange` handler. This will render a read-only field. If the field should be mutable use `defaultValue`. Otherwise, set either `onChange` or `readOnly`.
- As the warning explains change the code to use defaultValue<form onSubmit={handleSubmit}><input+ defaultValue="initial value"type="text" ref={input} /><button>Submit</button></form>
- Refresh the page
- The warning goes away
- Use
defaultValue
to initializeuncontrolled components
- Use
value
to initializecontrolled components
Class Component Example
File Input Example
In HTML, an <input type="file">
lets the user choose one or more files from their device storage to be uploaded to a server or manipulated by JavaScript via the File API.
In React, an <input type="file" />
is always an uncontrolled component because its value can only be set by a user, and not programmatically.
You should use the File API to interact with the files. The following example shows how to create a ref to the DOM node to access file(s) in a submit handler:
Function Component Example
Class Component Example
See the reference links below for a more complete example of a file upload component in React.
Items App Demo (CRUD) (continued)
Below we continue to expand on the Items (CRUD) Demo to use forms and do additional component communication. See the requirements listed below as well as the solution code.
Requirements
- Implement the feature to add an item
- create a Form component
- Add a text input and button to it
- render the Form in the Container by adding it above the list
- Note: since render needs to return one parent element you will need to wrap
<Form>
and<List>
in an outer<div>
or<React.Fragment>
- Note: since render needs to return one parent element you will need to wrap
- read the value from the input when you click the add button
- Add a feature to update an item inline
- Add an edit button to each item in the list
- Display an input and a button inline in place of the item when they click edit
- Save the update back into state in the app component
- Add a cancel link and use it to cancel out of editing mode. See the finished solution code below:
Solution (using Function Components & Hooks)
At this point, we are not calling an API yet we are just working with in-memory data but we will get to that next.