DEV Community

Gosha Arinich
Gosha Arinich

Posted on • Originally published at goshakkk.name on

Should a React form for editing update the underlying model?

In an imaginary world where forms are only ever used to enter something from scratch, what you know about forms in React might be enough. In the real world, however, forms are often lively creatures – you can come back to them and change your data.

Up until now, our forms were pretty stand-alone and isolated.

Forms for entering new data start out like this:

constructor() {
  super();
  this.state = {
    email: '',
    password: '',
  };
}
Enter fullscreen mode Exit fullscreen mode

and they are used something like this, perhaps:

<SignUpForm />
Enter fullscreen mode Exit fullscreen mode

We can see they don’t receive any props, they keep the current values in the state, and they initialize the state with empty values. Which is perfect if all you need is to collect brand new data through the form.

But what if you had a different form… for editing a post, for example. It would somehow have to accept current title and body. Maybe like this:

<PostForm title={someTitle} body={someBody} />
Enter fullscreen mode Exit fullscreen mode

Except now there are several possibilities as of how to go further with this:

  • Make the form itself fully controlled with regard to its parent. It means instead of keeping its own state, the form is always going to receive field values, as well as callbacks to change them. Just like with controlled/uncontrolled inputs, this will make an entire form controlled.
  <PostForm
    title={...}
    body={...}
    onChangeTitle={...}
    onChangeBody={...}
  />
Enter fullscreen mode Exit fullscreen mode

This is not a usual case, however. Only a subtle fraction of forms out there needs to have something like that. It could be used when the changes to the form, as you make them, need to be immediately reflected in the rest of the app.

And it’s not without its drawbacks, either. Doing it this way means invalid unsaved data now has a way out of the form: if you are directly updating some global state with work-in-progress form values, your app may end up consuming partial or errneous data.

  • Initialize the form state via props. The form will still have its own state with input values. It will simply use the passed field values as a starting point.

Used as:

  <PostForm title={someTitle} body={someBody} />
Enter fullscreen mode Exit fullscreen mode

The form is going to take initial props, which will be passed to its constructor, and set initial state based on them:

  class PostForm extends React.Component {
    constructor(props) {
      super(props);
      this.state = {
        title: props.title || '',
        body: props.body || '',
      };
    }
  }
Enter fullscreen mode Exit fullscreen mode

In a way, this is similar to making the form “uncontrolled” with respect to its parent. It maintains its own state, and uses the passed props as default values of the fields.

New values should be communicated to the parent when the form is submitted:

  <PostForm
    title={someTitle}
    body={someBody}
    onSubmit={(newTitle, newBody) => { ... }}
  />
Enter fullscreen mode Exit fullscreen mode

Unlike with individual inputs, having a form “uncontrolled” is a preferred method to make a form. A form is not a way to change a thing directly, rather, it’s a request to change one.

Think about paperwork to change the name. It doesn’t directly change your name as you fill it in; it doesn’t “bind” to your name. It is just initialized with some data (your current name), and you fill in the new name and take it to bureaucrats before it has any effect.

And the same is often true of forms on the web – they do something only when submitted.

This post was originally published on goshakkk.name


I blog about forms in React specifically, and other React-related things.
If you like what you see here, subscribe here to make sure you don't miss out on my next post.

Top comments (0)