Hi guys, Today we are going to build a simple form which take input from user and we log the data in console. We gonna build reusable inputs, which makes our code look clean and easy to build forms.
so let's start coding...
Demo Video
Source Code
Create a New React App
npx create-react-app react-forms
cd react-forms
npm start
I'am using material-ui/core package, It is optional . If you wanna follow along with me install this package by this command.
npm install @material-ui/core
App.css
.App {
width: 100vw;
height: 100vh;
background-color: #f5f5f5;
}
.flex {
display: flex;
justify-content: center;
align-items: center;
}
.column {
flex-direction: column;
}
.form {
display: flex;
flex-direction: column;
width: 350px;
padding: 10px;
}
.input {
width: 100% !important;
margin: 5px 0 !important;
}
Create textInput component
/src/components/common/textInput.js
import TextField from "@material-ui/core/TextField";
const TextInput = ({ ...rest }) => {
return (
<TextField
variant="outlined"
size="small"
className="input"
{...rest}
/>
);
};
export default TextInput;
...rest = You're simply pulling off the rest of the properties defined on your props object into a new argument called rest.
Create selectInput component
/src/components/common/selectInput.js
import TextField from "@material-ui/core/TextField";
const SelectInput = ({ options, ...rest }) => {
return (
<TextField
variant="outlined"
size="small"
className="input"
select
{...rest}
SelectProps={{ native: true }}
>
<option defaultValue="" style={{ display: "none" }}></option>
{options.map((option) => (
<option key={option} value={option}>
{option}
</option>
))}
</TextField>
);
};
export default SelectInput;
Create radioInput component
/src/components/common/radioInput.js
import Radio from "@material-ui/core/Radio";
import RadioGroup from "@material-ui/core/RadioGroup";
import FormControlLabel from "@material-ui/core/FormControlLabel";
import FormLabel from "@material-ui/core/FormLabel";
const RadioInput = ({ label, options, required, ...rest }) => {
return (
<div style={{ justifyContent: "space-between" }} className="flex input">
<FormLabel component="legend">{label}</FormLabel>
<RadioGroup {...rest} row>
{options.map((option) => (
<FormControlLabel
value={option}
control={<Radio color="primary" required={required} />}
label={option}
key={option}
/>
))}
</RadioGroup>
</div>
);
};
export default RadioInput;
Create form component
/src/components/common/form.js
import React, { Component } from "react";
import Button from "@material-ui/core/Button";
import TextInput from "./textInput";
import RadioInput from "./radioInput";
import SelectInput from "./selectInput";
class Form extends Component {
state = { data: {} };
handleChange = ({ currentTarget: input }) => {
const data = { ...this.state.data };
data[input.name] = input.value;
this.setState({ data });
};
handleSubmit = (event) => {
event.preventDefault();
this.doSubmit();
};
renderTextInput(name, label, type = "text", required = true) {
const { data } = this.state;
return (
<TextInput
name={name}
value={data[name]}
type={type}
required={required}
label={label}
onChange={this.handleChange}
/>
);
}
renderRadioInput(name, label, options, required = true) {
const { data } = this.state;
return (
<RadioInput
name={name}
value={data[name]}
onChange={this.handleChange}
label={label}
options={options}
required={required}
/>
);
}
renderSelectInput(name, label, options, required = true) {
const { data } = this.state;
return (
<SelectInput
name={name}
value={data[name]}
options={options}
label={label}
required={required}
onChange={this.handleChange}
/>
);
}
renderSubmitBtn(name) {
return (
<Button
type="submit"
style={{ marginLeft: "auto" }}
variant="contained"
size="medium"
color="primary"
>
{name}
</Button>
);
}
}
export default Form;
Create profile component
/src/components/profile.js
import React from "react";
import Form from "./common/form";
import Card from "@material-ui/core/Card";
class Profile extends Form {
state = {
data: { name: "", email: "", status: "", gender: "" },
};
doSubmit = () => {
console.log(this.state.data);
};
render() {
return (
<div className="flex column">
<h1>Profile</h1>
<form onSubmit={this.handleSubmit}>
<Card className="form">
{this.renderTextInput("name", "Name")}
{this.renderTextInput("email", "Email", "email")}
{this.renderSelectInput("status", "Marital Status", [
"Single",
"Married",
"Divorced",
"Widowed",
])}
{this.renderRadioInput("gender", "Gender", [
"Male",
"Female",
"Other",
])}
{this.renderSubmitBtn("Submit")}
</Card>
</form>
</div>
);
}
}
export default Profile;
App.js
import Profile from "./components/profile";
import "./App.css";
function App() {
return (
<div className="App flex">
<Profile />
</div>
);
}
export default App;
That's it test the form in browser, If you found any mistakes or making code better let me know in comment. For better understanding please watch Youtube video. Subscribe to my Youtube channel to get more knowledgeable content every week.
Arigato Gozaimasu.. 🙂
Top comments (4)
Enlightening read! Did you make the choice to use class components for Form and Profile for any special reason? Always curious about the thoughts behind the patterns.
Thanks for the write up!
I have used class components because it makes our code less and clean. Accessing parent functions in class component is easy as cake and if we want to change something in future there is only place to modify.
"it makes our code less and clean" - it's exactly the opposite - functional components create less footprint - both in a source code and in transpiled code, no more 'this' confusion, they're much easier to read and to test them...
I was wondering as well, considering the fact that hooks are popular this days. Really nice pattern though , would try it out using hooks.