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

@noaignite/formit

v3.1.0

Published

Form helper components built with React and MUI in mind.

Downloads

360

Readme

@noaignite/formit

Form helper hook & components built with React and MUI in mind. Heavily inspired by Formik.

Motivation

The main motivation was to create a standardized way of handling form fields using native browser APIs for the build footprint to be as small as possible. Validation is therefore meant to be implemented directly in the html with attributes like required, type="email", pattern="[0-9]{4}" and so on. Secondly, to have a simple way of using this library together with MUI.

Installation

Formit is available as an npm package.

// with npm
npm install @noaignite/formit

// with yarn
yarn add @noaignite/formit

Usage

There's two ways of using formit, the first being via the useFormit hook and secondly via the formit components. These being Formit, Form, Field & FormitConsumer. Let's look at how these can be implemented.

Using the hook

Using the useFormit hook is the simplest way to use formit and handle your forms. The initialValues key is mandatory, and is used for registering all the fields that you want to be controlled by formit. The validationErrors key is optional and can be used for printing error messages based on the validation rules you've defined in the html of each respective form field. The onSubmit callback will give you the controlled values as the first argument and actions as the second argument. In the example below we use the setSubmitting action to set formit into a loading state, momentarily disabling the submit button until the Promise has been resolved.

import * as React from 'react'
import { TextField } from '@mui/material'
import { useFormit } from '@noaignite/formit'

const BasicExample = () => {
  const { getFieldMeta, getFieldProps, isSubmitting, onSubmit } = useFormit({
    initialValues: {
      email: '',
      password: '',
    },
    validationErrors: {
      email: 'Please enter a valid email address',
      password: 'Password must contain an uppercase letter, a lowercase letter, and a number',
    },
    onSubmit: async (values, { setSubmitting /*, and more */ }) => {
      setSubmitting(true)
      await new Promise((resolve) => setTimeout(() => {
        console.log(values)
        resolve()
      }, 1000))
      setSubmitting(false)
    }
  })

  return (
    <form onSubmit={onSubmit}>
      <h1>My formit form</h1>
    
      <TextField
        type="email" // Email validation.
        required // Required validation.
        {...getFieldProps('email')} // Sets `name`, `value` & `onChange`.
        {...getFieldMeta('email')} // Sets Mui specific `error` and `herlperText`.
        label="Email"
      />

      <TextField
        pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" // Password validation.
        required // Required validation.
        {...getFieldProps('password')} // Sets `name`, `value` & `onChange`.
        {...getFieldMeta('password')} // Sets Mui specific `error` and `herlperText`.
        label="Password"
        type="password"
      />

      <button type="submit" disabled={isSubmitting}>Submit</button>
    </form>
  )
}

export default BasicExample

Using the context components

Using the context components is very similar to using the useFormit hook. The difference being that the formit state is saved to a React context using the Formit component, this is then consumed by formit's context components. The advantage to this approach is that your component, in this case BasicExample, won't have to re-render for each keystroke as the values & errors state updates. In the example below, <h1>My formit form</h1> won't re-render as you type into the the form fields.

A potential downside to this approach can be that it's more difficult to organize your logic. Try out the different approaches and use the one that suits your need the best.

import * as React from 'react'
import { TextField } from '@mui/material'
import { Formit, Form, Field, FormitConsumer } from '@noaignite/formit'

const BasicExample = () => {
  return (
    <Formit
      initialValues={{
        email: '',
        password: '',
      }}
      validationErrors={{
        email: 'Please enter a valid email address',
        password: 'Password must contain an uppercase letter, a lowercase letter, and a number',
      }}
      onSubmit={async (values, { setSubmitting /*, and more */ }) => {
        setSubmitting(true)
        await new Promise((resolve) => setTimeout(() => {
          console.log(values)
          resolve()
        }, 1000))
        setSubmitting(false)
      }}
    >
      <Form>
        <h1>My formit form</h1>

        <Field
          component={TextField} // Renders Mui TextField.
          name="email" // Sets values from `getFieldProps` & `getFieldMeta` based on `name`.
          type="email" // Email validation.
          required // Required validation.
          label="Email"
        />

        <Field
          component={TextField} // Renders Mui TextField.
          name="password" // Sets values from `getFieldProps` & `getFieldMeta` based on `name`.
          pattern="^(?=.*\d)(?=.*[a-z])(?=.*[A-Z])(?!.*\s).*$" // Password validation.
          required // Required validation.
          type="password"
          label="Password"
        />

        <FormitConsumer>
          {({ isSubmitting }) => (
            <button type="submit" disabled={isSubmitting}>Submit</button>
          )}
        </FormitConsumer>
      </Form>
    </Formit>
  )
}

export default BasicExample

The Field component

Worth mentioning regarding the Field component is that it also accepts a function as it's children prop, exposing field specific formit state and functions. This can be handy in those cases where one might need to tweak the behaviour, for example for the Mui Datepicker where the onChange argument does not expose the event as it's argument.

<Field name="date">
  {({ field, setValue }) => (
    <DatePicker
      label="Date"
      name={field.name}
      value={field.value}
      onChange={(newValue) => setValue(newValue)}
    />
  )}
</Field>

Preparing your data for the API request

A lot of times your form data will need to comply with a certain data structure that the recipient API requires. In these cases formit can help you out by allowing you to predefine the data structure of your form values so it's ready to be sent to the API. Let's look at how this can be done.

import * as React from 'react'
import { Checkbox, FormControlLabel, TextField } from '@mui/material'
import { Formit, Form, Field, FormitConsumer } from '@noaignite/formit'

const BasicExample = () => {
  return (
    <Formit
      initialValues={{
        user: {
          firstName: '',
          lastName: '',
          phoneNumbers: ['', ''],
        },
        newsletter: false,
      }}
      onSubmit={(values) => {
        /*
        `values` would then be structured like below:
        {
          user: {
            firstName: 'Jon',
            lastName: 'Doe',
            phoneNumbers: ['111111111', '222222222'],
          },
          newsletter: true,
        }
        */
        console.log(values)
      }}
    >
      <Form>
        <h1>My formit form</h1>

        <Field
          component={TextField}
          name="user.firstName" // Path to formit state
          label="First name"
        />

        <Field
          component={TextField}
          name="user.lastName" // Path to formit state
          label="Last name"
        />
        
        <Field
          component={TextField}
          name="user.phoneNumbers[0]" // Path to formit state
          label="Primary phone number"
        />
        
        <Field
          component={TextField}
          name="user.phoneNumbers[1]" // Path to formit state
          label="Secondary phone number"
        />
        
        <Field
          component={FormControlLabel}
          control={<Checkbox />}
          name="newsletter" // Path to formit state
          label="Newsletter"
        />

        <FormitConsumer>
          {({ isSubmitting }) => (
            <button type="submit" disabled={isSubmitting}>Submit</button>
          )}
        </FormitConsumer>
      </Form>
    </Formit>
  )
}

export default BasicExample

Sourcing values from API call

Sometimes, the "initial values" you want to init formit with originates from an API endpoint. This means that the desired initial values would be defined after formit has initialized. To solve this, one needs to pass the enableReinitialize prop to formit. This ensures that formit will reinitialize whenever the values of the initialValues change.

Below, you have an example of this being done with the context components from formit:

const BasicExample = () => {
  const [initialValues, setInitialValues] = React.useState({
    email: '',
    password: '',
  })

  React.useEffect(() => {
    const fetch = async () => {
      // Faked API call, for demonstration.
      await new Promise((resolve) =>
        setTimeout(() => {
          const apiOriginatedInitialValues = {
            email: '[email protected]',
            password: '****',
          }

          setInitialValues(apiOriginatedInitialValues)
          resolve()
        }, 1000),
      )
    }

    fetch()
  }, [setInitialValues])

  return (
    <Formit
      initialValues={initialValues}
      enableReinitialize
    >
      {/* ... */}
    </Formit>
  )
}

Or using the hook:

// ...
const formit = useFormit({
  enableReinitialize: true
})
// ...