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

me-form-backend

v0.8.8

Published

compose awesome forms

Downloads

64

Readme

me-form-backend

compose awesome forms

Get Started

yarn add me-form-backend
// or
npm install me-form-backend

Basic Example

import Form, { useFormItem, FormItem, ArrayField } from 'me-form-backend'

const Input = props => {
  const { value, onChange, rest } = useFormItem(props)
  return <input {...rest} value={value} onChange={onChange} />
}

const App = () => (
  <Form
    initValue={{ f1: { a: 'a' }, f2: [{ text: 'foo' }, { text: 'bar' }] }}
    onSubmit={formData => {}}
  >
    <FormItem name="f1">
      <Input name="a" />
    </FormItem>
    <ArrayField name="f2">{() => <Input name="text" />}</ArrayField>
    <button>submit</button>
  </Form>
)

Form

Form is the root of your form context, it provides form data to FormItem, useFormItem & ArrayField.

Props:

| Prop | Description | | ------------- | ------------------------------------------------------------------------------------------------------- | | children | React elements rendered in Form or render prop: ({submit, value, error, validate}) => {} | | validator | a callback called before onSubmit, return null for no error or an error descriptor | | onSubmit | callback called when form submit and all validators pass, receive the form data as argument | | value | pass form data from parent component, Form with value and changed props is in controlled mode | | onChange | callback used in combination with "value" and is called when form data changes | | initValue | initial value for uncontrolled mode | | defaultValue | gets merged into initValue or value on some undefined field, usefull for some field is missing in value | | formTag | bool type, whether render an html form tag, default false | | errorMessages | an object, which keys are validator rules and values are corresponding error messages |

Controlled vs Uncontrolled

Form, much like React controls, provides two modes: controlled and uncontroled:

Controlled, pass value & onChange to Form component, onChange will be called whenever form data changes.

<Form value={...} onChange={...}>...</Form>

Uncontrolled, pass initValue to Form component, you can get form data in submit callback.

<Form initValue={...}>...</Form>

Why defaultValue can't be set on fields

Because currently in React there's not a good way that a parent component can read props from descendant components, so in the first render the defaultValue prop of a field can't be available from value of ancestors. This can be confusing and this is against the data flow model of React.

useFormItem

useFormItem is a React custom hook. It connects a form control component with Form context

Input Options

Usualy we just pass all props to useFormItem hook as input is just fine, useFormItem forwards all unknown props, but you may want to control it, all used props listed below:

| property | description | | ------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | name | string for field name | | validator | callback whenever field value changes, return null as no error, or an error descriptor, besides field value, if second argument is true, it means the validator is called by submit | | errorMessages | an object, which keys are validator rules and values are corresponding error messages | | requied | bool, shorthand for required validator | | minLength | number, shorthand for min length validator | | label | ReactNode, it will be passed to dynamic error message function | | parse | a function called when field value changes, receive changed value, can be used like trim input or parse date from string | | format | a function called when pass value to control component, receive current value, can be used when format date to string | | interceptor | [Deprecated] use parse instead. A function called when field value changes, receive changed value, can be used like trim input | | didUpdate | a callback that called when a field is confirmed to change, receive changed value and a patch fucntion |

didUpdate v.s. interceptor

  • interceptor is called right before a field value is changed, and it only provide the changed value. It's easy to use, but only can change the changed value
  • didUpdate is called before the changed value is committed, aka set the Form internal state in uncontrolled mode or call onChange prop of Form in controlled mode. It is provide the changed value and a patch function for the Form value, you can use the patch function change or remove any other fields of the Form, it's useful when you want to accomplish associated field changing
<Form initValue={{a: "", b: ""}}>
<Input
  ...
  name="a"
  didUpdate={(changedValue, patch) => {
    if (changedValue) {
      patch({b: "b also changed", c: "c added"}) // when a changing to true, set b and add c
    } else {
      patch({b: ""}, "c") // when a changing to false, reset b, and remove c
    }
  }}
/>
</Form>

Output Options

| property | description | | ---------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | | value | field value | | onChange | onChange callback | | error | error descriptor of current field, null if no error | | resetError | a callback to clear current error | | label | identical label passed to the hook | | id | a dot concated string of all ancestors' name and the current field name, you can pass it to as for attribute and a control id attribute to associate a and a control |

FormItem & ArrayField

FormItem and ArrayField are designed to handle middle layer fields, like object or array.

FormItem

FormItem can be used to describe a object field, which transform context value to a sub-field.

<Form initValue={{ obj: { a: 'a', b: 'b' } }}>
  <FormItem name="obj">
    <Input name="a" />
    <Input name="b" />
  </FormItem>
</Form>

It receives all options of useFormItem as props.

ArrayField

ArrayField is a shorthand for iterate over an array field, which uses children as render prop for each item's render.

In the childern render, context is transformed to each item.

<Form initValue={{ arr1: ['a', 'b'], arr2: [{text: 'foo'}, {text: 'bar'}}] }}>
  <div>arr1</div>
  <ArrayField name="arr1">
    {() => <Input />}  // you can get item value in arr1 without name prop, because context has been transformed to each item
  </ArrayField>
  <div>arr2</div>
  <ArrayField name="arr2">
    {() => <Input name="text"/>} // you can get item properties of arr2 by name prop, because context has been transformed to each item
  </ArrayField>
</Form>

/*
render as
<form>
  <div>arr1</div>
  <input value="a"/>
  <input value="b"/>
  <div>arr2</div>
  <input name="text" value="foo"/>
  <input name="text" value="bar"/>
</form>
*/

Validation & Error

Form, FormItem and useFormItem all receive a validator for form validation.

Validator should receive form data or the field value it has been defined, returns null if no error or error descriptor if any error exists.

ErrorDescriptor

  • rule defines the type of the error, it's used to find error message or to decide whether the error is ralated a field.
  • message can be a plain string, if not provided, error message will be computed from errorMessages.
  • labels is an array of field labels passed to message format funcion above metioned, if not provided, field label prop will be used.
interface ErrorDescriptor {
  rule: string
  message?: string
  labels?: ReactNode[] // string or react element
}

;<Form
  initValue={{ a: 'a', b: 'b' }}
  validator={({ a, b }) =>
    a !== b ? { rule: 'same value', message: 'a should be same as b' } : null
  }
>
  <Input name="a" />
  <Input name="b" />
</Form>

ErrorMessages

ErrorMessages is an object, which keys are validator rules and values are corresponding error messages.

Value can be a plain string or a function which receive field labels and return a string.

ErrorMessages can be config at multiple level: Form, FormItem or Control, it is merged top down, a later one overrides smae rule.

Trigger timing

Form validation is different from fields validation at trigger timing.

Form validation is triggered when form is to be submitted, and it triggers all fields' validators first, and at last is the validator defined at Form.

Other validators, defined at FormItem or useFormItem, is triggered when the field value changes.

Recipes

parse & format

const DateInput = () => (
  <Input
    parse={date => date.format('YYYY-MM-DD')}
    format={s => moment(s, 'YYYY-MM-DD')}
  />
)