0.3.0 • Published 6 years ago

formulary v0.3.0

Weekly downloads
2
License
MIT
Repository
github
Last release
6 years ago

☄️ Formulary - React Forms ☄️· GitHub license npm version CircleCI

React Forms package offers easy to use forms with validations. It uses the new React Context API that was introduced in React 16.3.0. React Forms handles the form state for you and offer simply API to read/write to the state.

Useful link

Changelog: List of changes

Roadmap (future plans): List of features

Motivation

There already is a great library for managing forms, called react-redux. It uses Redux to keep the state. The advantage of using Redux is when:

  • you need to interact with your form data globally from your application
  • you use Redux time travel features to record user interaction

You can read more when you should use Redux in article You Might Not Need Redux from Dan Abramov.

But in the most cases you don´t need those capabilities or you use other state management library. So why should you buy into Redux just to create forms? You shouldn´t and that is the reason this library exists.

Instalation

npm install --save formulary

Peer dependencies

Make sure you have installed React and React DOM in the version 16.3 or higher.

npm install --save react react-dom

Examples

This example shows a simple form.

First we need to define how are the inputs displayed (i.e. twitter bootstrap, material-ui/react-toolbox or your custom components) Let´s define a simple input that only displays a given value and that handles value change (without validation or error messages)

const MyInput = props => (
  <input
    type={props.type}
    value={props.field.value}
    onChange={e => props.onChange(e.target.value)}
    onBlur={props.onBlur}
  />
);

Here we define the actual form component, the only thing we need to pass in is a way of handling the submitting. In our case onSubmit is a simple action that performs an remote API call to our backend.

Note that our Field components can be nested several levels deep and still have access to the form state thanks to the React Context API.

import { Form, Field } from 'formulary';

const MyForm = props => {
  onSubmit = (values, state) => {
    // Call backend with your favourite request library
    return axios.post('/api/login', values).then(
      res => {
        // ...handle result
      },
      err => {
        // ...handle errors, i.e.
        // you can set form field errors by rejecting the promise
        // and returning structure { fields: { <field-key>: [ ... ] }}
        return Promise.reject({
          fields: {
            username: ['Login credentials are not valid'],
          },
        });
      }
    );
  };

  return (
    <Form onSubmit={onSubmit}>
      <Field name="username" label="Jméno" value="JohnDoe">
        <MyInput type="text" />
      </Field>

      <Field name="password" label="Jméno" value="SecretPassword123">
        <MyInput type="password" />
      </Field>

      <input type="submit" value={'Submit'} />
    </Form>
  );
};

API

Field

Each Field component is passed these props.

Field handlers:

KeyDescriptionSignature
onChangeHandles input changes, accepts the new input valueonChange(value)
onFocusFocus event handleronFocus(event)
onBlurBlurring event handler, upon blurring the field is marked as touchedonBlur(event)
onResetResets the field to its initial valueonReset()

Field values:

KeyTypeDescription
value*Current field value
initialValue*Initial value
metaObject
meta.keystringField name
meta.labelstringField label (is passed to the validation functions)
meta.pristinebooleanUser has NOT interacted with the field yet
meta.dirtybooleanUser has interacted with the field
meta.touchedbooleanField has lost focus
meta.untouchedbooleanField has NOT yet lost focus
meta.validbooleanAll validation rules passes
meta.invalidbooleanSome of the validation rules are not met
meta.validatingbooleanValidation in progress
functionsObject
functions.parsefunctionParsing function (i.e. (value) => moment(value))
functions.transformfunctionTransformating function (i.e. (value) => value.toUpperCase())
functions.formatfunctionFormatting function (i.e. (value) => moment.format('YYYY-MM-DD'))
functions.validateArray\<function>Array of validation functions
errorsArray\<string>Array of error messages from validation rules

Value lifecycle

onChange(value) -> parse(value) -> transform(value) -> { state } -> format(value)

Form

KeyDescriptionSignature
onSubmitWill be fired only when the form is valid. Returning a rejected promise can be used to set errors on fields.onSubmit(values, state)

Validation

Using the default validation functions

Notice we are using different syntax for inputs, instead of passing children we are giving the component as a prop (component) and we pass other props from the Field component (type="text"). Both ways are equvivalent.

import { isEmail, isRequired } from '@alesmenzel/react-forms';

// Pass in a message
const email = isEmail('Invalid email address');
// Pass in a function that returns a message
const required = isRequired(field => `Field ${field.meta.label} is required`);

const MyForm = ({ onSubmit }) => {
  return (
    <Form onSubmit={onSubmit}>
      <Field
        name="email"
        label="Email"
        validate={email}
        component={MyInput}
        type="text"
      />

      <Field
        name="another_email"
        label="Required email"
        validate={[required, email]}
        component={MyInput}
        type="text"
      />

      <input type="submit" value={'Submit'} />
    </Form>
  );
};

As you can see the validate prop accepts both functions and an array of functions. In case of array, all rules must be met.

Built in validators

Numbers: Name | Description | Signutare --- | --- | --- minimum | Check whether the value is at least min | minimum(msg, min) maximum | Check whether the value is at most max | maximum(msg, max) range | Check whether the value is at least min and at most max | range(msg, min, max) isNumber | Check whether the value is a finite number | isNumber(msg) isInteger | Check wheteher the value is an integer (a whole number) | isInteger(msg) isFloat | Checks whether the value is a float | isFloat(msg)

String:

NameDescriptionSignutare
minimumLengthChecks if value has at least min lengthminimumLength(msg, min)
maximumLengthChecks if value has at most max lengthmaximumLength(msg, max)
lengthChecks if value is at least min and at most max lengthlength = (msg, min, max)
isEqualChecks whether the value is equal to comparisonisEqual(msg, comparison)
isNotEqualChecks whether the value is not equal to comparisonisNotEqual(msg, comparison)
isInChecks whether the value equals to one of comparisonsisIn(msg, comparisons)
isNotInChecks whether the value does not equal to any of comparisonsisNotIn(msg, comparisons)
patternChecks whether the value matches pattern, flags are optional (and can be passed with the pattern directly /[A-Z]+/gi)pattern(msg, pattern, flags)

Required:

NameDescriptionSignutare
isRequire.dChecks whether a value is set, note that zero is considered a valid valueisRequired(msg)

Email:

NameDescriptionSignutare
isEmailChecks for a valid email address (uses isemail module)isEmail(msg)

Using custom validation functions

You can also create a custom validator function. Here is a sample:

Validation functions should return error message if the rule is not met othervise return undefined.

import { getFunction, getMessage } from 'formulary'

export const isNumberEight = msg => {
  // enable passing a message or a function that returns a message
  const getMessage = getFunction(msg);

  return (field) => {
    const { value } = field;

    if (value !== 8) {
      return getMessage(field, { metadata: 'you can pass some metadata down' });
    }

    return undefined;
}

Contributing

The main goal of this repository is to make working with react forms easy without forcing to use other technologies, like Redux.

Code of Conduct

We adopted a Code of Conduct that we expect project participants to adhere to. Please read the full text so that you can understand what actions will and will not be tolerated.

License

This project is licensed under the terms of the MIT license.