All Projects → zero-plus-x → Neoform

zero-plus-x / Neoform

Licence: mit
✅ React form state management and validation

Programming Languages

javascript
184084 projects - #8 most used programming language

Projects that are alternatives of or similar to Neoform

Formhelper
ASP.NET Core - Transform server-side validations to client-side without writing any javascript code. (Compatible with Fluent Validation)
Stars: ✭ 155 (-4.32%)
Mutual labels:  validation, form-validation, form
React Form With Constraints
Simple form validation for React
Stars: ✭ 117 (-27.78%)
Mutual labels:  validation, form-validation, form
Approvejs
A simple JavaScript validation library that doesn't interfere
Stars: ✭ 336 (+107.41%)
Mutual labels:  validation, form-validation, form
formalizer
React hooks based form validation made for humans.
Stars: ✭ 12 (-92.59%)
Mutual labels:  validation, form, form-validation
Form Validation.js
The most customizable validation framework for JavaScript.
Stars: ✭ 127 (-21.6%)
Mutual labels:  validation, form-validation, form
svelte-form
JSON Schema form for Svelte v3
Stars: ✭ 47 (-70.99%)
Mutual labels:  validation, form, form-validation
Legit
input validation framework
Stars: ✭ 81 (-50%)
Mutual labels:  validation, form-validation, form
React Hook Form
📋 React Hooks for form state management and validation (Web + React Native)
Stars: ✭ 24,831 (+15227.78%)
Mutual labels:  validation, form-validation, form
React Final Form
🏁 High performance subscription-based form state management for React
Stars: ✭ 6,781 (+4085.8%)
Mutual labels:  validation, form-validation, form
Formsy React
A form input builder and validator for React JS
Stars: ✭ 708 (+337.04%)
Mutual labels:  validation, form-validation, form
react-drip-form
☕ HoC based React forms state manager, Support for validation and normalization.
Stars: ✭ 66 (-59.26%)
Mutual labels:  form, higher-order-component, hoc
Usetheform
React library for composing declarative forms, manage their state, handling their validation and much more.
Stars: ✭ 40 (-75.31%)
Mutual labels:  validation, form-validation, form
Resolvers
📋 Validation resolvers: Zod, Yup, Joi, Superstruct, and Vest.
Stars: ✭ 222 (+37.04%)
Mutual labels:  validation, form-validation, form
Bootstrap Validate
A simple Form Validation Library for Bootstrap 3 and Bootstrap 4 not depending on jQuery.
Stars: ✭ 112 (-30.86%)
Mutual labels:  validation, form-validation, form
Redux Form
A Higher Order Component using react-redux to keep form state in a Redux store
Stars: ✭ 12,597 (+7675.93%)
Mutual labels:  validation, form-validation, form
Bunny
BunnyJS - Lightweight native (vanilla) JavaScript (JS) and ECMAScript 6 (ES6) browser library, package of small stand-alone components without dependencies: FormData, upload, image preview, HTML5 validation, Autocomplete, Dropdown, Calendar, Datepicker, Ajax, Datatable, Pagination, URL, Template engine, Element positioning, smooth scrolling, routing, inversion of control and more. Simple syntax and architecture. Next generation jQuery and front-end framework. Documentation and examples available.
Stars: ✭ 473 (+191.98%)
Mutual labels:  validation, form-validation, form
Ok
✔️ A tiny TypeScript library for form validation
Stars: ✭ 34 (-79.01%)
Mutual labels:  validation, form-validation, form
Just Validate
Lightweight (~4,5kb gzip) form validation in Javascript Vanilla, without dependencies, with customizable rules (including remote validation), customizable messages and customizable submit form with ajax helper.
Stars: ✭ 74 (-54.32%)
Mutual labels:  validation, form-validation, form
Flagged
Feature Flags for React made easy with hooks, HOC and Render Props
Stars: ✭ 97 (-40.12%)
Mutual labels:  higher-order-component, hoc
Ngx Dynamic Form Builder
FormBuilder + class-transformer + class-validator = dynamic form group builder for Angular10+
Stars: ✭ 93 (-42.59%)
Mutual labels:  validation, form

travis travis


Better form state management for React where data state is directly mapped to form fields, so form becomes just a representation and changing interface for that data state.

Usage

Intro

Let's say you have some data and you want to represent it as an HTML form with an Input for each data field.

"user": {
  "name": "Pepe",
  "status": "sad",
  "friends": [
    "darkness"
  ]
}

Each data field can be referenced with a "key" or "property" path. You might be familiar with this concept from working with immutable data structures or helpers like lodash.get().

"user": {
  "name": "Pepe",  // "user.name"
  "status": "sad", // "user.status"
  "friends": [
    "darkness"     // "user.friends.0"
  ]
}

The first core idea of NeoForm is to map data to form fields using these key/property paths. We'll refer to this data as "form state" below.

Let's see how it works with a step-by-step example. First, we need to install the following set of dependencies:

yarn add prop-types recompose neoform neoform-validation neoform-plain-object-helpers

We'll start with creating a simple input:

field

const MyInput = () => (
  <input/>
);

export default MyInput;

After wrapping this input with field HOC from NeoForm we'll have:

value and onChange props

A value from a form state (can be used in checkbox as a checked attribute if it's boolean, and so on) and onChange handler to let NeoForm know that value should be changed:

import { field } from 'neoform';

const MyInput = ({ value, onChange }) => (
  <input
    value={value}
    onChange={(e) => onChange(e.target.value)}
  />
);

export default field(MyInput);

Use (e) => e.target.checked if you have a checkbox or just (value) => value if you have some custom/3rd-party field implementation.

form

Now when the input is ready we can use it in a form:

import MyInput from '../MyInput';

const MyForm = () => (
  <form>
    <MyInput name="user.name"/>
    <MyInput name="user.status"/>
    <MyInput name="user.friends.0"/>
  </form>
);

export default MyForm;

Let's connect this form to NeoForm by wrapping it with a form HOC:

import { form } from 'neoform';

import MyInput from '../MyInput';

const MyForm = () => (
  <form>
    <MyInput name="user.name"/>
    <MyInput name="user.status"/>
    <MyInput name="user.friends.0"/>
  </form>
);

export default form(MyForm);

App

Finally, we assemble everything together:

import { setValue, getValue } from 'neoform-plain-object-helpers';

import MyForm from '../MyForm';

class App extends Component {
  constructor(props) {
    super(props);

    this.state = {
      data: props.data
    };
    this.onChange = this.onChange.bind(this);
    this.onSubmit = this.onSubmit.bind(this);
  }

  onChange(name, value) {
    this.setState((prevState) => setValue(prevState, name, value));
  }

  onSubmit() {
    console.log('submit:', this.state.data);
  }

  render() {
    <MyForm
      data={this.state.data}
      getValue={getValue}
      onChange={this.onChange}
      onSubmit={this.onSubmit}
    />
  }
}

What's going on here? As you may guessed, all fields in NeoForm are controlled. So, in order to update them, we need to update data state:

getValue

First, we need to specify getValue prop to tell NeoForm how exactly it should retrieve field value from data state. The reason to do that is because you might have a plain object data, Immutable or something else with a different "interface".

Instead of writing your own getValue function, you can use one from neoform-plain-object-helpers or neoform-immutable-helpers package.

getValue arguments:

  • data — form data state
  • name — field name

setValue

Second, we have only one onChange handler for the entire form instead of multiple ones for each field. So, whenever some field requests a change, we need to update form data by updating the state so updated value is passed to that field with a new render.

ℹ️ Consider using Recompose pure() HOC or React.PureComponent for fields to avoid unnecessary renders and get performance boost in some cases.

Instead of writing your own handler, you can use setValue helper from neoform-plain-object-helpers or neoform-immutable-helpers package.

setValue arguments:

  • data — form data state
  • name — field name
  • value — new field value
+--------------+
|              |
|              |
|    +---------v---------+
|    |                   |
|    |    MyForm.data    |
|    |                   |
|    +---------+---------+
|              |
|       name   |
|              |
|    +---------v---------+
|    |                   |
|    |   MyInput.value   |
|    |                   |
|    +---------+---------+
|              |
|              |
|    +---------v---------+
|    |                   |
|    | MyInput.onChange  |
|    |                   |
|    +---------+---------+
|              |
|       name   |  value
|              |
|    +---------v---------+
|    |                   |
|    |  MyForm.onChange  |
|    |                   |
|    +---------+---------+
|              |
|       name   |  value
|              |
+--------------+

Validation

Validation in NeoForm is always asynchronous.

fieldValidation

fieldValidation is another HOC:

import { field } from 'neoform';
import { fieldValidation } from 'neoform-validation';

const MyInput = ({
  validate,
  validationStatus,
  validationMessage,
  ...props
}) => (
  <input {...props} onBlur={validate} />
  {
    validationStatus === false && (
      <span>{validationMessage}</span>
    )
  }
)

export default field(fieldValidation(MyInput));

Where the props are:

  • validate – validation action, can be called whenever you want (onChange, onBlur, etc)
  • validationStatustrue | false | undefined status of field validation
  • validationMessage – an optional message passed from validator

formValidation

import { form } from 'neoform';
import { formValidation } from 'neoform-validation';

import MyInput from '../MyInput';

const MyForm = ({
  /* data, */
  validate,
  validationStatus,
  onInvalid,
  onSubmit
}) => (
  <form onSubmit={(e) => {
    validate(onSubmit, onInvalid)
    e.preventDefault();
  }}>
    <MyInput name="user.name"/>
    <MyInput name="user.status"/>
    <MyInput name="user.friends.0"/>
  </form>
);

export default form(formValidation(MyForm));

Where:

  • validate – entire form validation action: it will validate all fields and if they're valid it will invoke a first provided callback (onSubmit handler in most cases) or second callback (something like onInvalid) if they're invalid
  • validationStatustrue | false | undefined status of entire form validation

Validators

"Validator" is just a Promise. Rejected one is for validationStatus: false prop and resolved is for validationStatus: true. An optional argument passed to a rejected or fulfilled Promise becomes validationMessage prop.

export const requiredValidator = (value, type) => {
  if (value === '') {
    return Promise.reject('💩');
  }

  return Promise.resolve('🎉');
};

Where:

  • value – field value for validation
  • type – event type. Can be submit, change, blur or anything you will provide to field validate method

It's up to you how to manage multiple validators — with a simple Promise.all() or some complex asynchronous sequences — as long as validator returns a single Promise.

To use a validator you should just pass it in a validator prop to an individual field:

import { requiredValidator } from '../validators'

// …

<form>
  <MyInput name="user.name" validator={requiredValidator} />
  <MyInput name="user.status"/>
  <MyInput name="user.friends.0"/>
</form>

// …

📺 Check out live demo.

FAQ

But this is just like my entire form is a single component with a single onChange!

Right.

Does it affect performance because of re-rendering entire form on every field change?

Probably in some cases it does. But as it was mentioned here before consider using Recompose pure() HOC or React.PureComponent to avoid that.

What about Redux?

Absolutely same approach: call an action on form onChange and then use plain/immutable helper to return updated data state from a reducer.

Status

This is a monorepo composed of these packages:

package version description
neoform npm Core toolkit with form and field HOCs
neoform-validation npm formValidation and fieldValidation HOCs
neoform-plain-object-helpers npm getValue and setValue helpers for plain object state
neoform-immutable-helpers npm getValue and setValue helpers for Immutable state

Development

  1. Create a new folder in packages/, let's say neoform-foo.
  2. See package.json in already existing packages and create new neoform-foo/package.json.
  3. Put source code in neoform-foo/src/, it will be transpiled and bundled into neoform-foo/dist/, neoform-foo/lib/ and neoform-foo/es/.
  4. Put tests written with Jest in neoform-foo/test/.
  5. Put demo in neoform-foo/demo/, it will be rendered and wrapped with HMR.

Available scripts using Start:

yarn start build <package>
yarn start demo <package>
yarn start test
yarn start testWatch
yarn start lint

Available demos:

yarn start demo neoform
yarn start demo neoform-validation
Note that the project description data, including the texts, logos, images, and/or trademarks, for each open source project belongs to its rightful owner. If you wish to add or remove any projects, please contact us at [email protected].