DEV Community

Cover image for How To Create Forms In React ? - Controlled Inputs & Material UI
CyberWolves
CyberWolves

Posted on • Edited on

How To Create Forms In React ? - Controlled Inputs & Material UI

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

Project Structure
react-forms-project-structure

Create a New React App

npx create-react-app react-forms
cd react-forms
npm start
Enter fullscreen mode Exit fullscreen mode

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
Enter fullscreen mode Exit fullscreen mode

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;
}
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

...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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

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;
Enter fullscreen mode Exit fullscreen mode

App.js

import Profile from "./components/profile";
import "./App.css";

function App() {
    return (
        <div className="App flex">
            <Profile />
        </div>
    );
}

export default App;
Enter fullscreen mode Exit fullscreen mode

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)

Collapse
 
ash_bergs profile image
Ash

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!

Collapse
 
cyberwolves profile image
CyberWolves

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.

Collapse
 
piotrlewandowski profile image
Piotr Lewandowski

"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...

Collapse
 
lucciddev profile image
lucciddev

I was wondering as well, considering the fact that hooks are popular this days. Really nice pattern though , would try it out using hooks.