An Introduction to Using Form Elements in React
An Introduction to Using Form Elements in React
React form components 101: What you need to know
Forms admittedly are not the most fun things to code in web development, but after exploring some of the tools offered in the React community, combined with a component based structure that comes naturally with React, I am beginning to relax somewhat when complex forms are required — it is easier to build, manage and validate form inputs with the range of tools React offers than say a PHP or Python solution.
So in this article we will explore the following:
- How to code bare-bones form elements in JSX within your React components
- Use the
onChange
prop, as well as introducingdefaultValue
anddefaultChecked
props used to update the element status. - Placing each form element in it’s separate component that utilises
PropTypes
- Submitting a form and next steps, where I will briefly mention packages on validation and form management tools that are out the scope of this article. Once you have a solid foundation of form elements in React, moving onto validation tools (
There are indeed differences between coding HTML form elements vs JSX form elements. Let’s start exploring these differences. Think state and props!
Form Elements in React Need State
If you have visited forms before in React without using a form management package (that wrap a lot of functionality into their own components, which comes with advantages and disadvantages), you may have realised that some of the elements do not update when they seemingly should. the textarea, checkbox and radio are prime examples. What we need in these cases is to link state to these elements: when the state changes, the elements re-render with their updated status (checked, unchecked, selected, or simply a value or textares).
Note on using global state for form elements:
You may read on Stackoverflow that some developers advise linking form elements to your global Redux state. This is unnecessary. It is very likely that your form state will not be required outside of that form, therefore the additional overhead that storing a bunch of checkbox statuses in your Redux state will just add more complexity that you can avoid.When would you use global state for forms? Well, there are not many scenarios. In the event that a form is interrupted by a different UI, you would want to save the form state so the user will not have to repeatedly fill the form again. But then again, requiring a user to return to a form after initiating the completion of it is a bad design choice.
Checkboxes, Radios and Textareas need state in order to update their values.
Coding the form elements bare-bones
Let’s check out what happens when we code some basic form elements inside a React component. At this stage we will not introduce any state to these elements. Below is a component that renders a simple form.
Copy and paste this into your React project to inspect the elements.
export class ExploreForms extends React.Component {
render() { return(
<div>
<h2>Form elements</h2><form action="/myform" method="post">
<p>Textbox: <br /><input type="text" name="test" /></p>
<p>Textarea: <br /><textarea name="textarea_1" value="My Textarea" /></p>
<p>Checkbox: <br /><input type="checkbox" name="category" value="category1" /> Cat1<input type="checkbox" name="category" value="category2" /> Cat2<input type="checkbox" name="category" value="category3" /> Cat3</p>
<p>Radio: <br /><input type="radio" name="location" value="location1" /> Loc1<input type="radio" name="location" value="location2" /> Loc2<input type="radio" name="location" value="location3" /> Loc3</p>
<p>Select: <br /> <select name="product" defaultValue={2}> <option value="1">Option 1</option> <option value="2">Option 2</option> <option value="3">Option 3</option> </select></p>
<input type="submit" value="Submit Form" /></form></div>
); }}
That was quite a lot of non-functional markup! At this stage you can either test this code yourself, or take my word that the following behaviours occur.
Let’s quickly run through the behaviour of each element before moving on:
- Textbox: The textbox appears to be working as expected. When we type with the textbox being active, the value updates, but nothing is committed to state to reflect the current value of the text field.
- Textarea: We have run into our first issue. The textarea is not updating as we type. To see changes, this element requires state — we will explore how to tie it’s value to state in the next section, and also use a defaultValue prop to store the elements’ value.
- Checkboxes: Another element that stubbornly will not check or uncheck itself! But not to worry, that is what React’s state is for. Not only do we need state for checkboxes, but we also use the defaultChecked prop that differs from what you are used to using in HTML.
- Radios: Again, the radio list is not updating when I attempt to change the selected value. State is required here too, as well as using a defaultChecked prop that will reflect whether the element is selected or not.
- Select dropdowns: Select dropdowns are working as expected, but like a text field, nothing is committed to state.
Although it would be required to link the textbox and select values to some state in order to work with them, we will not be doing so just yet.
Let’s fix up the textarea, checkbox and radio elements.
Introducing onChange, defaultValue, and defaultChecked props
Let’s focus on the textarea, checkbox and radio components, as these seem to be the most severely disabled in the React universe.
In order to get these elements to be responsive, we need to utilise the onChange
prop to update the element state as we interact with it. In order to do this, it makes sense to create separate components for each form element which you can use throughout your projects as exportable components.
JSX also uses defaultValue
and defaultChecked
props to handle the values of these components. The following gist wraps our 3 form elements into their respective components, with some additional React intelligence with PropTypes
thrown in:
Let’s run through some of of this code that is constructing our form element components.
Textarea
render () { const handleChange = event => { this.setState({value: event.target.value}); }
return ( <textarea id={this.props.id} name={this.props.name} defaultValue={this.state.value} onChange={this.handleChange} /> );}
Within the textarea component, we use defaultValue
which in turn displays this.state.value
— the only variable our state contains. We also utilise onChange
with handleChange
function to update the state on every onChange event.
This is JSX and not HTML, so we do not wrap the textarea value within the textarea tag — it is self closing. Instead we use defaultValue.
Radio
render () { const handleChange = () => { this.setState({selected: !this.state.selected}) }
return ( <span> <input type="radio" defaultChecked={this.state.selected} onChange={this.handleChange} id={this.props.id} name={this.props.name} /> {this.props.label} </span> ); }
We adopt the same onChange
prop and handleChange
function with radio elements, but this time use the defaultChecked
prop for the value. And as you can see, defaultChecked
requires a boolean, which can either be true or false. Therefore when we are toggling the state, we can simply pass the negative falue of the current value, with !this.state.selected
.
Note: We do not need to store external state to sync an entire radio list! The list will automatically update as we switch to a new value. We may however want to pass the value to the parent component which will most likely be managing your form validation.
Checkbox
render () { const handleChange = () => { this.setState({checked: !this.state.checked}) }
return ( <span> <input type="checkbox" defaultChecked={this.state.checked} onChange={this.handleChange} id={this.props.id} name={this.props.name} /> {this.props.label} </span> ); }
Checkboxes work strikingly similar to the radio component. Nothing new here!
Working with PropTypes and form elements
Forms are a good use case for PropTypes
and defaultProps
features in React. The warnings in your Javascript consoles will become increasingly valuable as your forms become more complex.
Before using PropTypes, remember to import them into your script. (prop-types is bundled with React so there is no need to install this separately):
import PropTypes from 'prop-types';
PropTypes are a good use case when working with form elements — they will give us warnings when the incorrect data type is passed through to the component in development mode. isRequired
will also flag a warning if such a value is not supplied.
defaultProps
are also useful for form elements, if we want an initial value to be configured, defaultProps
is a great way to do this.
Submitting a Form
Let’s now visit submitting a form.
This is rather simple in React, no additional components needed here:
export class MyForm extends React.Component { handleSubmit = (e) => { e.preventDefault(); console.log('submitting form. We need to validate it!'); }
render() { return( <form action="/myform" method="post" onSubmit={this.handleSubmit}>
//components here
<input type="submit" value="Submit Form" />
</form> ) }}
The key takeaway here is that our submit handler function prevents the form from redirecting to another URL: we prevent this default behaviour with e.preventDefault()
, which gives us the opportunity to validate our form without any redirects or refreshes. The event
being triggered by submitting our form is being passed to handleSubmit
automatically: we do not need to pass an event argument at the onSubmit
prop.
Where to go from here
What we have not discussed in this article is how to collate all the form values from our form and validate them.
We could just inject a handler function as a separate prop into our form components to pass the value to my parent state as it is updated. That is perfectly valid.
But there is a better way
However, this is where I recommend using packages that do a wonderful job at form management and validation. Diving into these tools warrant a separate article, but let’s summarise what to explore from here:
Visit the official React form documentation, athttps://reactjs.org/docs/forms.htmlto get a comprehensive overview of how each element is implemented. This will be a breeze after solidifying your base understanding from this article.
Before diving into these tools and resources, try wrapping the textbox
and select
form elements into their own components, and expand them in any way you wish that suites your project requirements.