We need to create a React component that renders a "Contact Us" form with the following fields:
The form should use controlled inputs with React state to manage the form values.
First, we'll create a new file called ContactUs.jsx with a basic form structure:
// ContactUs.jsx
function ContactUs() {
return (
<div>
<h2>Contact Us</h2>
<form>
<div>
<label htmlFor="name">Name:</label>
<input id="name" type="text" />
</div>
<div>
<label htmlFor="email">Email:</label>
<input id="email" type="text" />
</div>
<div>
<label htmlFor="phone">Phone:</label>
<input id="phone" type="text" />
</div>
<button>Submit</button>
</form>
</div>
);
}
export default ContactUs;
Then update the App component to render our ContactUs component:
// App.jsx
import ContactUs from './ContactUs';
function App() {
return (
<ContactUs />
);
}
export default App;
Now, let's add state variables using the useState hook for each form field:
// ContactUs.jsx
import { useState } from 'react';
function ContactUs() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [phoneType, setPhoneType] = useState('');
const [comments, setComments] = useState('');
return (
<div>
<h2>Contact Us</h2>
<form>
<div>
<label htmlFor="name">Name:</label>
<input id="name" type="text" />
</div>
<div>
<label htmlFor="email">Email:</label>
<input id="email" type="text" />
</div>
<div>
<label htmlFor="phone">Phone:</label>
<input id="phone" type="text" />
</div>
<button>Submit</button>
</form>
</div>
);
}
Next, we'll connect each input to its corresponding state variable using the value attribute and onChange event handler:
// ContactUs.jsx
import { useState } from 'react';
function ContactUs() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [phoneType, setPhoneType] = useState('');
const [comments, setComments] = useState('');
return (
<div>
<h2>Contact Us</h2>
<form>
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
type="text"
onChange={e => setName(e.target.value)}
value={name}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
type="text"
onChange={e => setEmail(e.target.value)}
value={email}
/>
</div>
<div>
<label htmlFor="phone">Phone:</label>
<input
id="phone"
type="text"
onChange={e => setPhone(e.target.value)}
value={phone}
/>
</div>
<button>Submit</button>
</form>
</div>
);
}
Now, let's add the phone type select dropdown and comments textarea field:
// ContactUs.jsx
// ...existing code...
<div>
<label htmlFor="phone">Phone:</label>
<input
id="phone"
type="text"
onChange={e => setPhone(e.target.value)}
value={phone}
/>
<select
name="phoneType"
onChange={e => setPhoneType(e.target.value)}
value={phoneType}
>
<option value="" disabled>
Select a phone type...
</option>
<option>Home</option>
<option>Work</option>
<option>Mobile</option>
</select>
</div>
<div>
<label htmlFor="comments">Comments:</label>
<textarea
id="comments"
name="comments"
onChange={e => setComments(e.target.value)}
value={comments}
/>
</div>
Finally, let's implement the form submission handler:
// ContactUs.jsx
import { useState } from 'react';
function ContactUs() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [phoneType, setPhoneType] = useState('');
const [comments, setComments] = useState('');
const onSubmit = e => {
// Prevent the default form behavior so the page doesn't reload.
e.preventDefault();
// Create a new object for the contact information.
const contactUsInformation = {
name,
email,
phone,
phoneType,
comments,
submittedOn: new Date()
};
console.log(contactUsInformation);
// Reset the form state.
setName('');
setEmail('');
setPhone('');
setPhoneType('');
setComments('');
};
return (
<div>
<h2>Contact Us</h2>
<form onSubmit={onSubmit}>
{/* Form fields here */}
<button>Submit</button>
</form>
</div>
);
}
Here's the complete ContactUs component with all features implemented:
// ContactUs.jsx
import { useState } from 'react';
function ContactUs() {
const [name, setName] = useState('');
const [email, setEmail] = useState('');
const [phone, setPhone] = useState('');
const [phoneType, setPhoneType] = useState('');
const [comments, setComments] = useState('');
const onSubmit = e => {
// Prevent the default form behavior so the page doesn't reload.
e.preventDefault();
// Create a new object for the contact information.
const contactUsInformation = {
name,
email,
phone,
phoneType,
comments,
submittedOn: new Date()
};
console.log(contactUsInformation);
// Reset the form state.
setName('');
setEmail('');
setPhone('');
setPhoneType('');
setComments('');
};
return (
<div>
<h2>Contact Us</h2>
<form onSubmit={onSubmit}>
<div>
<label htmlFor="name">Name:</label>
<input
id="name"
type="text"
onChange={e => setName(e.target.value)}
value={name}
/>
</div>
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
type="text"
onChange={e => setEmail(e.target.value)}
value={email}
/>
</div>
<div>
<label htmlFor="phone">Phone:</label>
<input
id="phone"
type="text"
onChange={e => setPhone(e.target.value)}
value={phone}
/>
<select
name="phoneType"
onChange={e => setPhoneType(e.target.value)}
value={phoneType}
>
<option value="" disabled>
Select a phone type...
</option>
<option>Home</option>
<option>Work</option>
<option>Mobile</option>
</select>
</div>
<div>
<label htmlFor="comments">Comments:</label>
<textarea
id="comments"
name="comments"
onChange={e => setComments(e.target.value)}
value={comments}
/>
</div>
<button>Submit</button>
</form>
</div>
);
}
export default ContactUs;
In this tutorial, we've created what are known as controlled components. Here's what that means:
In HTML, form elements like <input>, <textarea>, and <select> maintain their own internal state. When a user interacts with these elements, they update their own state without any help from JavaScript.
In React, we prefer to keep all state in our component, making it the "single source of truth." We do this by:
value attribute of form elements to a state variableonChange event to update that state variable when the user interacts with the elementThis approach gives us complete control over the form's behavior and makes it easier to:
Here's the flow of what happens with controlled components:
<input> elementonChange event is triggerede.target.value and update our state variable<input> element's value attribute is set to the updated state valueThis creates a complete cycle where the component state controls the input value, not the other way around.
Forms are essential in web applications. Some common uses include:
The concepts you've learned (controlled inputs, form submission handling) are used in all these scenarios and are foundational for building interactive web applications.
Here are some ways you could enhance this form:
// Add state for validation errors
const [validationErrors, setValidationErrors] = useState({});
// Validate form before submission
const validateForm = () => {
const errors = {};
if (!name) errors.name = "Name is required";
if (!email) errors.email = "Email is required";
if (email && !email.includes('@')) errors.email = "Please enter a valid email";
if (phone && !phoneType) errors.phoneType = "Phone type is required if phone is provided";
return errors;
};
const onSubmit = e => {
e.preventDefault();
// Check for validation errors
const errors = validateForm();
if (Object.keys(errors).length > 0) {
setValidationErrors(errors);
return; // Don't submit if there are errors
}
// Clear any existing errors
setValidationErrors({});
// Rest of the submission logic...
};
Then you would display validation errors below each field:
<div>
<label htmlFor="email">Email:</label>
<input
id="email"
type="text"
onChange={e => setEmail(e.target.value)}
value={email}
/>
{validationErrors.email && (
<p className="error">{validationErrors.email}</p>
)}
</div>