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

@nicolastoulemont/use-form

v0.2.3

Published

Generic use-form utility hook with onChange, onBlur, onSubmit listeners possibilities

Downloads

4

Readme

Use-form

This use-form utility hook aims to facilitate the handling of complex forms validation and form field addition or removal.

Disclaimer

This is a work in progress tool and mainly aimed at my personal use accross my projects.

Installation

npm install @nicolastoulemont/use-form
or
yarn add @nicolastoulemont/use-form

Usage

  • Basic usage

The useForm hooks is designed withincremental / partial usage of its features in mind. As such, if the form doesn't require validation or fields manipulation it can be used without overhead

export function BasicComponent() {
  const { values, onChange } = useForm()
  return (
    <div>
      <label>Email</label>
      <input
        name="email"
        value={values['email'] || ''}
        onChange={({ target: { name, value } }) => onChange({ name, value })}
      />
    </div>
  )
}
  • Field level validation

In order to perform field level validation useForm hook require a object param containing the fields options array and optionally the initial values. The field options must include at least a name property but can be extended to include other field related information such as its label, placeholder, type, etc.

interface Field extends FieldOptions {
  label?: string
}

const required = (value: any) =>
  typeof value === 'undefined' || value === '' ? 'Required' : undefined

const hasAbc = (value: any) => (value.includes('abc') ? 'Has abc' : undefined)

const initialFields: Array<Field> = [
  {
    name: 'email',
    listener: {
      onChange: [hasAbc],
    },
  },
  {
    name: 'password',
    listener: {
      onBlur: [required],
    },
  },
  {
    name: 'address',
    listener: {
      onSubmit: [required],
    },
  },
  {
    name: 'first_name',
    label: 'first_name_label',
  },
]
  • Validation functions

The listener object allow the user to insert an array of functions to be executed either on onChange, onBlur or onSubmit. Theses functions have access to the field value, the form values, the form errors, the form fields, the form hasSubmitted status as params, in this order.

The return value of validations functions is stored on the errors object with the field name as key.

function validationFn(fieldValue, values, errors, fields, hasSubmitted) {
  // Set validation rule(s)
}
  • Fields manipulation

If given an initial fields array, the useForm return a fields array that can then be used as following :

interface Field extends FieldOptions {
  label?: string
}

export function BasicComponent() {
  const required = (value: any) =>
    typeof value === 'undefined' || value === '' ? 'Required' : undefined

  const hasAbc = (value: any) => (value.includes('abc') ? 'Has abc' : undefined)

  const initialFields: Array<Field> = [
    {
      name: 'reset',
      listener: {
        onChange: [() => resetFields()],
      },
    },
    /* ...fields */
    {
      name: 'address',
      listener: {
        onSubmit: [required],
      },
    },
  ]

  const { values, setValues, errors, setErrors, fields, onChange, onBlur, onSubmit } = useForm({
    initialFields,
  })

  function handleSubmit() {
    onSubmit()
  }

  return (
    <div>
      {fields.map(field => (
        <div key={field.name}>
          {field.label && <label>{field.label}</label>}
          <input
            name={field.name}
            value={values[field.name] || ''}
            onChange={({ target: { name, value } }) => onChange({ name, value })}
            onBlur={({ target: { name, value } }) => onBlur({ name, value })}
          />
          {errors[field.name] && <div>{errors[field.name]}</div>}
        </div>
      ))}
    </div>
  )
}

The useForm hooks also expose 5 fields manipulation methods on the Fields array

  • removeFields
function removeFields(names: Array<string> | string): void

This function will filter out one or more fields based on their names

  • addFields
function addFields(newFields: Array<T>, index: number): void

This function insert one or more fields at the give index

  • changeField
function changeField(fieldOptions: Partial<T>, name: string): void

This function will update a field selected by name with the given fieldOptions.

  • moveField
function moveField(from: number, to: number): void

This function will move a field from one index to another

  • resetFields
function resetFields(): void

This function will reset the fields to the initialFields array

Api

The useForm hooks returns the following :

  • values: The state values as an object.
{ [field.name]: value }
  • setValues: The React dispatch function updating the values state.
  • errors: The errors as an object. The return value of the onChange and onBlur listeners functions are stored in the errors state.
{ [field.name]: value }
  • setErrors: The React dispatch function update the errors state.
  • onChange: Update the values state and run the field onChange listeners.
export interface EventInfos {
  name: string
  value: any
}

function onChange(infos: EventInfos) {
  hasSubmitted && setHasSubmitted(false)
  const { name, value } = infos
  if (errors[name]) setErrors({ ...errors, [name]: undefined })

  setValues({
    ...values,
    [name]: value,
  })

  if (record[name].listener?.onChange) {
    setErrors({
      ...errors,
      [name]: runFns(record[name].listener?.onChange as Array<Function>, value),
    })
  }
}
  • onBlur: Run the field onBlur listeners.
export interface EventInfos {
  name: string
  value: any
}

function onBlur(infos: EventInfos) {
  const { name, value } = infos

  if (record[name].listener?.onBlur) {
    setErrors({
      ...errors,
      [name]: runFns(record[name].listener?.onBlur as Array<Function>, value),
    })
  }
}
  • onSubmit: Run all the fields onChange and onSubmit listeners. It also return the form validity (boolean) and the errors count.
function onSubmit() {
  setHasSubmitted(true)

  let count = 0
  let submitErrors: { [key: string]: any } = {}

  fields.forEach(field => {
    if (field.listener) {
      const fns = getFns(field.listener)
      const errors = runFns(fns, values[field.name])
      submitErrors[field.name] = errors
      if (errors) {
        count++
      }
    }
  })
  setErrors(submitErrors)
  return [count === 0, count]
}
  • hasSubmitted (boolean): The submit state of the form. Set to false by the onChange function.
  • deleteVal: Helper function to delete a specific value in the values state.
function deleteVal(key: string) {
  setValues(values => ({ ...values, [key]: undefined }))
}
  • deleteErr: Helper function to delete a specific error in the errors state.
function deleteErr(key: string) {
  setErrors(errors => ({
    ...errors,
    [key]: undefined,
  }))
}
  • resetValues: Helper function to reset the values state to an empty object.
const resetValues = () => setValues({})
  • resetErrors: Helper function to reset the errors state to an empty object.
const resetErrors = () => setErrors({})
  • resetForm: Helper function to reset both the errors and values states to an empty object.
function resetForm() {
  setErrors({})
  setValues({})
}
  • removeFields: Remove one or more field from the fields array
function removeFields(names: Array<string> | string) {
  if (typeof names === 'string') {
    setFields(fields => fields.filter(field => field.name !== names))
  } else {
    setFields(fields => fields.filter(field => names.indexOf(field.name) === -1))
  }
}
  • addFields: Add one or more field to the fields array
function addFields(newFields: Array<FieldOptions>, index: number) {
  const updatedFields = [...fields.slice(0, index), ...newFields, ...fields.slice(index)]
  setFields(updatedFields)
}
  • changeField: Update one field
function changeField(fieldOptions: Partial<FieldOptions>, name: string) {
  const updatedRecord = { ...record, [name]: { ...record[name], ...fieldOptions } }
  const updatedFields = recordToArray(updatedRecord)
  setFields(updatedFields)
}
  • moveField: Move a field in the array
function moveField(from: number, to: number) {
  const newFields = [...fields]
  newFields.splice(to, 0, newFields.splice(from, 1)[0])
  setFields(newFields)
}
  • resetFields: Set the fields back to the initialFields array.
const resetFields = () => setFields(initialFields)

Built with

Versionning

This tool use SemVer for versioning.

Licence

MIT