ControlledForm Component


(Taber McFarlin) #1

Hi! I’m new to React, so as a first project for myself I thought I would make basically a bill split thing. That part of what I have to show isn’t flushed out at all yet, but what I wanted to show off is the ControlledForm component. I know there’s more robust form generators out there, but this works as if you’re just making a normal HTML form, just with the added attribute of what values you need returned.

I made this for fun and learned a lot about React doing it, and I’m just curious if other people think it’s cool!

This is a simple form defined with the component, it looks almost exactly like a basic HTML form.

<ControlledForm onSubmit={this.handleNewPerson} values={["name"]}>
  <label htmlFor="personName">
    Name:
    <input id="personName" name="name" type="text" value="" />
  </label>
  <input type="submit" value="Submit" />
</ControlledForm>

Becomes this:

<form onSubmit=bound handleSubmit()>
  <label key="personNameLabel" for="personName">
    "Name:"
    <input key="personName" id="personName" name="name" type="text" value="">
  </label>
  <input type="submit" value="Submit">
</form>

The ControlledForm looks through the elements in the form and looks for one with the name “name”, because it is in the values array in props. When it finds the input element with the name “name” it creates a new element with the same props and just an added onChange function.
return React.createElement(type, props);
Then the label element is also recreated with the same string, and now the new input element as children

return React.createElement(
  type,
  props,
  controlledChildren === children ? children : controlledChildren
);

I used React.createElement() here so I could save the type as a variable and create the new element with it. To my knowledge I couldn’t do this with JSX.
The submit button and any other components that do not need to be controlled, and have no children that need to be controlled, are left existing in the form. Radio buttons and checkboxes are also supported; here is a more complicated example that uses checkboxes.

function NewItemForm(props) {
  const { people, onSubmit } = props;
  const peopleChecks = people.map(person => (
    <li key={person}>
      <label htmlFor={person}>
        <input
          id={`${person}Check`}
          name="people"
          type="checkbox"
          value={person}
        />
        {person}
      </label>
    </li>
  ));

  return (
    <ControlledForm
      onSubmit={onSubmit}
      values={["name", "price", "count", "people"]}
    >
      <label htmlFor="productName">
        Name:
        <input id="productName" name="name" type="text" value="" />
      </label>
      <label htmlFor="productPrice">
        Price:
        <input
          id="productPrice"
          name="price"
          type="number"
          min="0"
          value="0"
          step="0.01"
        />
      </label>
      <label htmlFor="productCount">
        Count:
        <input id="productCount" name="count" type="number" min="1" value="1" />
      </label>
      <ul key="peopleBuyingList">People buying: {peopleChecks}</ul>
      <input type="submit" value="Submit" />
    </ControlledForm>
  );
}

All this creates the two forms that interact with each other and function exactly as one would expect.
48%20AM