DEV Community

Kuvam Bhardwaj
Kuvam Bhardwaj

Posted on

Creating custom Forms in React with stateless inputs

Introduction

Ever seen something like this?
stateful input elements

In this article, i will be trying to solve this problem. So lets get started!

Problem

Its a tedious task to make big forms with managed state of each of the input. Imagine a form with 10 more fields 🤯
stateful tedious arrangement of elements

What if we could contain the input elements in a parent element and manage their state within a single source of truth? That would be awesome right. Lets build the parent component!

Solution

Lets create a React Component & call it Form and pass the input elements as its children. To recall, we know that the children of a component could be accessed by the children prop of the component which is just an array of its children.
children passed in parent element example

If we console.log each child in the Form component, it looks something like this
each child of the children prop passed into parent element

Now, we desire an output from the Form component in a Javascript Object in form of key-value pairs corresponding to fieldnames and their respective input values.
If we could alter the value prop and handle the onChange prop of the element, our mission will be accomplished!

But wait... how do we know while mapping, what fieldname we are on? and where & how to store the data of an input when it changes?

To resolve this problem, we'll give an additional prop to the child elements called key (another fancy default prop of a react element check its use here. we'll be using the key just to indicate the fieldname here).
Also passing 2 extra props (formData & setFormData) in Form component

import { useState } from "react";
import Form from "./Form";
import "./styles.css";

export default function App() {

  const [formData, setFormData] = useState(null)

  return (
    <div className="App">
      <Form setFormData={setFormData} formData={formData}>
        <input key='name' placeholder='Enter name' />
        <input key='email' placeholder='Enter email' />
        <input key='phoneNumber' placeholder='Enter phone' />
        <input key='address' placeholder='Enter address' />
      </Form>

      <button onClick={() => console.log(formData)}>Submit</button>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

In the Form component, we create a new array by mapping the children array and altering the props field.
value of the element is taken from formData variable and onChange function is mapped to another function which changes the field's value by using the key (being accessed by child.key) and stores in the formData via setFormData

export default function Form({ children, formData, setFormData }) {

  const handleInputChange = (key, text) => {
    let newFormData = { ...formData }
    newFormData[key] = text
    setFormData(newFormData)
  }

  const mappedChildren = children.map(child => {
    return {
      ...child,
      props: {
        ...child.props,
        onChange: e => handleInputChange(child.key, e.target.value),
        value: formData ? formData[child.key] : ''
      }
    }
  })

  return (
    <section>
      {mappedChildren}
    </section>
  )
}
Enter fullscreen mode Exit fullscreen mode

The component is complete, lets check its working by logging formData on the console
lovely professional input for output

lovely professional output

IT WORKS!

Latest comments (0)