0.3.0 • Published 6 years ago

react-form-state v0.3.0

Weekly downloads
-
License
ISC
Repository
-
Last release
6 years ago

Form-State

A straightforward form management

It's all about you already know about controlling the state inside a component, but made easier to write expressive data-driven forms.

You will not have to learn a new way of doing things as many libraries out there does today. Form-State can be seen as just as a small package that helps you with forms without extra overheading components.

Built on top of some lodash/fp functions, this library provides immutability out of the box with.

Background

This library is built with these principles in mind:

  • Keep data immutable
  • Avoid extra components, render functions or new syntax
  • Configuration over code
  • setState only when needed
  • Keep track on form errors
  • Only send the changes made
  • DRY

That being said and in order to keep a straightforward form component, the first thing we do is define the schema that the form is based on (configuration over code). To achieve that, we use Joi to write it down, but also to normalize and validate the data. It's a very powerful library with a clean syntax that makes you understand all of your form should provide in a breeze. https://github.com/jeffbski/joi-browser

Installation

yarn add react-form-state

Usage

  import Form from 'react-form-state';

Coupling the form state inside a component state is just as easy as defining three objects in it.

  state = {
    form: {},
    changes: {},
    errors: {}
  };

See? No magic. You can also use a shortcut

  state = {
    ...Form.defaultState
  };

The form object keeps the raw user input. changes holds all the data that was modified and is valid! errors is, well, the errors. Next, provide a schema that your form is based on.

  schema = {
    firstName: Joi.string().min(5).required(),
    email: Joi.string().email().required(),
    age: Joi.number().min(0).max(100).required()
  }
class ProfileForm extends React.Component {
  constructor(props) {
    super(props);
    this.form = new Form({ schema });
    this.state = { ...Form.defaultState };
  }
}

Form-State manages a small state inside of it. Its changes reflects back to the component state. Thus, let's provide a hook that make those changes happen. It's not required though, all form methods returns a new state object that you can manually merge into yours component state, if you want to. You are in charge.

class ProfileForm extends React.Component {
  constructor(props) {
    super(props);
    this.form = new Form({ schema, onChange: this.onFormChange });
    this.state = { ...Form.defaultState };
  }

  onFormChange = (formState) => this.setState({ ...formState });
}

formState wraps form, changes and errors as we defined before. A shallow merge into the component state is just fine, as Form-State already handle nested changes and creates new objects everytime they have been modified, guaranteeing immutability.

Finally, let's bring the form and put it all togheter.

class ProfileForm extends React.Component {
  constructor(props) {
    super(props);
    this.form = new Form({ schema, onChange: this.onFormChange });
    this.state = { ...Form.defaultState };
  }

  onFormChange = (formState) => this.setState({ ...formState });

  render() {
    const { errors, form: profile } = this.state;
    const canSubmit = this.form.hasChanges() && !this.form.hasErrors();
    return (
      <div>
        <label for="firstName">First Name {errors.firstName}</label>
        <input name="firstName" value={profile.firstName}
          onChange={this.form.handleChange} />

        <label for="email">Email {errors.firstName}</label>
        <input name="email" value={profile.email}
          onChange={this.form.handleChange} />    

        <label for="age">Email {errors.age}</label>
        <input name="age" value={profile.age}
          onChange={this.form.handleChange} />

        {canSubmit && <button onClick={this.handleSubmit}>Submit!</button>}
      </div>
    )
  }
}

Your handleSubmit function can be simple as just grabbing the changes object and dispatching it to the server. For example, if you are using redux:

  handleSubmit = () => {
    const { changes } = this.state;
    this.props.dispatch(saveProfile(changes));
  }

Peforming setState manually on form changes

Form-State holds a brief state so every time a field changes, it can perform all tasks involved to validate the data, merge the errors, raw and the normalized input to, finally call the trigger that will eventually call setState in the component. This is done all by once, so the setState will be called only once also, and you won't have to write waterfall setState's for every possible change.

On the other hand, you can easily get to the bare metal here.

  handeChange = (event) => {
    const { name, value } = event.target;
    const newState = this.form.set(name, value);
    this.setState({ ...newState });
  }

As set peforms triggers the input validation, you can also merge the data you want, if you need bypass any validation.

  checkName = (name) => {
    if (name === 'john') {
      const errors = { firstName: 'already exists!' };
      const newState = this.form.merge({ errors });
      this.setState({ ...newState });
    }
  }

Or even update (replace) a whole form state. Be in mind that with great powers comes great resposabilities.

Common pitfalls

Keep in mind that every time you manually peform a changing in the form state that you want both, the user see the changes feedback and set these changes to be commited when submitting the form, you need to merge/update form and changes sub states.

If you are adding extra data in the form state that is not defined in the schema, this library won't track its errors and so will not clear them by itself, if you are setting any. You will have to manage it too.

Nevertheless, you don't need to be worry about using the onFormChange trigger and managing some extra data by yourself. react-form-state will only clear errors for fields that are defined on the schema.

Initializing the form

Custom error messages

Dynamic inputs

0.3.0

6 years ago

0.2.2

6 years ago

0.2.1

6 years ago

0.2.0

6 years ago

0.1.3

6 years ago

0.1.2

6 years ago

0.1.1

6 years ago

0.1.0

6 years ago

0.0.21

6 years ago

0.0.20

6 years ago

0.0.2

6 years ago

0.0.1

6 years ago