Creating controlled inputs

In this section, we'll start to create our form containing our first controlled input:

  1. Create a new file called ContactUs.tsx in the src folder containing the following code:
import * as React from "react";

const ContactUs: React.SFC = () => {
return (
<form className="form" noValidate={true}>
<div className="form-group">
<label htmlFor="name">Your name</label>
<input type="text" id="name" />
</div>
</form>
);
};

export default ContactUs;

This is a function component that renders a form containing a label and an input for the user's name.

  1. We have referenced some CSS classes, so let's add these to the bottom of index.css:
.form {
width: 300px;
margin: 0px auto 0px auto;
}

.form-group {
display: flex;
flex-direction: column;
margin-bottom: 20px;
}

.form-group label {
align-self: flex-start;
font-size: 16px;
margin-bottom: 3px;
}

.form-group input, select, textarea {
font-family: Arial;
font-size: 16px;
padding: 5px;
border: lightgray solid 1px;
border-radius: 5px;
}

The form-group class wraps each field in our form, displaying the label above the input with nice spacing.

  1. Let's reference our form from our page now. Go to ContactUsPage.tsx and import our component:
import ContactUs from "./ContactUs";
  1. We can then reference our component in the render method at the bottom of the div container:
<div className="page-container">
<h1>Contact Us</h1>
<p>If you enter your details we'll get back to you as soon as we can.</p>
<ContactUs />
</div>

If we look at the running app and go to the Contact Us page, we'll see our name field rendered:

We can enter our name into this field, but nothing will happen yet. We want the entered name to be stored in the ContactUsPage container component state. This is because ContactUsPage will eventually manage the form submission.

  1. Let's add a state type to ContactUsPage:
interface IState {
name: string;
email: string;
reason: string;
notes: string;
}

class ContactUsPage extends React.Component<{}, IState> { ... }

As well as the person's name, we are going to capture their email address, reason for contacting the shop, and any additional notes.

  1. Let's also initialize the state in a constructor:
public constructor(props: {}) {
super(props);
this.state = {
email: "",
name: "",
notes: "",
reason: ""
};
}
  1. We now need to get the name value from the state in ContactUsPage into the ContactUs component. This will allow us to display the value in the input. We can do this by first creating props in the ContactUs component:
interface IProps {
name: string;
email: string;
reason: string;
notes: string;
}

const ContactUs: React.SFC<IProps> = props => { ... }

We have created props for all the data we are going to eventually capture in our form.

  1. Now, we can bind the name input value to the name prop:
<div className="form-group">
<label htmlFor="name">Your name</label>
<input type="text" id="name" value={props.name} />
</div>
  1. We can now pass these from the state in ContactUsPage:
<ContactUs 
name={this.state.name}
email={this.state.email}
reason={this.state.reason}
notes={this.state.notes}
/>

Let's go to the running app and go to our Contact Us page. Try typing something into the name input.

Nothing seems to happen... something is preventing us from entering the value.

We have just set the input value to some React state, so React is now controlling the value of the input. This is why we no longer appear to be able to type into it.

We are part-way through creating our first controlled input. However, controlled inputs aren't much use if users can't enter anything into them. So, how can we make our input editable again?

The answer is that we need to listen to changes to the input value, and update the state accordingly. React will then render the new input value from the state.

  1. Let's listen to changes to the input via the onChange prop:
<input type="text" id="name" value={props.name} onChange={handleNameChange} />
  1. Let's create the handler we have just referenced as well:
const ContactUs: React.SFC<IProps> = props => {
const handleNameChange = (e: React.ChangeEvent<HTMLInputElement>) => {
props.onNameChange(e.currentTarget.value);
};
return ( ... );
};

Note that we've used the generic React.ChangeEvent command with the type of the element we are handling (HTMLInputElement).

The currentTarget prop in the event argument gives us a reference to the element that the event handler is attached to. The value property within this gives us the latest value of the input.

  1. The handler references an onNameChange function prop that we haven't defined yet. So, let's add this to our interface, along with similar props for the other fields:
interface IProps {
name: string;
onNameChange: (name: string) => void;
email: string;
onEmailChange: (email: string) => void;
reason: string;
onReasonChange: (reason: string) => void;
notes: string;
onNotesChange: (notes: string) => void;
}
  1. We can now pass these props from ContactUsPage into ContactUs:
<ContactUs
name={this.state.name}
onNameChange={this.handleNameChange}
email={this.state.email}
onEmailChange={this.handleEmailChange}
reason={this.state.reason}
onReasonChange={this.handleReasonChange}
notes={this.state.notes}
onNotesChange={this.handleNotesChange}
/>
  1. Let's create the change handlers we've just referenced in ContactUsPage that set the relevant state:
private handleNameChange = (name: string) => {
this.setState({ name });
};
private handleEmailChange = (email: string) => {
this.setState({ email });
};
private handleReasonChange = (reason: string) => {
this.setState({ reason });
};
private handleNotesChange = (notes: string) => {
this.setState({ notes });
};

If we now go to the Contact Us page in the running app and enter something into the name, this time the input behaves as expected.

  1. Let's add fields for email, reason, and notes in our render method for ContactUs:
<form className="form" noValidate={true} onSubmit={handleSubmit}>
<div className="form-group">
<label htmlFor="name">Your name</label>
<input type="text" id="name" value={props.name} onChange={handleNameChange} />
</div>

<div className="form-group">
<label htmlFor="email">Your email address</label>
<input type="email" id="email" value={props.email} onChange={handleEmailChange} />
</div>

<div className="form-group">
<label htmlFor="reason">Reason you need to contact us</label>
<select id="reason" value={props.reason} onChange={handleReasonChange}>
<option value="Marketing">Marketing</option>
<option value="Support">Support</option>
<option value="Feedback">Feedback</option>
<option value="Jobs">Jobs</option>
<option value="Other">Other</option>
</select>
</div>

<div className="form-group">
<label htmlFor="notes">Additional notes</label>
<textarea id="notes" value={props.notes} onChange={handleNotesChange} />
</div>
</form>

For each field, we render a label and the appropriate editor inside a div container, with a form-group class to space our fields out nicely.

All the editors reference handlers for handling changes to their value. All the editors also have their value set from the appropriate ContactUs prop. So, all the field editors have controlled components.

Let's have a closer look at the select editor. We set the value in the select tag using a value attribute. However, this doesn't exist in the native select tag. Usually, we have to include a selected attribute in the relevant option tag within the select tag:

<select id="reason">
<option value="Marketing">Marketing</option>
<option value="Support" selected>Support</option>
<option value="Feedback">Feedback</option>
<option value="Jobs">Jobs</option>
<option value="Other">Other</option>
</select>

React adds the value prop to the select tag, and manages the selected attribute on the option tag for us, behind the scenes. This allows us to consistently manage input, textarea, and selected in our code.

  1. Let's now create the change handlers for these fields that call the function props we created earlier:
const handleEmailChange = (e: React.ChangeEvent<HTMLInputElement>) => {
props.onEmailChange(e.currentTarget.value);
};
const handleReasonChange = (e: React.ChangeEvent<HTMLSelectElement>) => {
props.onReasonChange(e.currentTarget.value);
};
const handleNotesChange = (e: React.ChangeEvent<HTMLTextAreaElement>) => {
props.onNotesChange(e.currentTarget.value);
};

This completes our basic Contact Us form, using various controlled form elements. We haven't implemented any validation or submitted the form yet. We'll get to these later in the chapter.

We're already noticing lots of similar code for each field for getting changes to fields into state. In the next section, we are going to start work on a generic form component and switch to using this for our Contact Us form.

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset
3.144.243.184