npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

react-form-state

v0.3.0

Published

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

Downloads

54

Readme

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