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

multi-formik-hook

v2.0.0

Published

React hook for formik library. It helps to control multiple forms in one component.

Downloads

5,932

Readme

multi-formik-hook

Utility hook for the Formik library.
It helps manage multiple Formik instances by the single hook.

Requirements

  • React>=16.8.0 (for hooks)
  • Formik>=2.2.0 (it should work with previous versions, but it is not tested)

What it can do

  • Control multiple forms from one place. For example if you have several forms on the page, and you want to submit them all at once by a single button.
  • Provides a state of all controlled forms (like valid and dirty state).
  • Allows to dynamically add or remove forms on demand.
  • Can help with performance in a big forms. On user input only one form will be re-rendered at a time.

What it can't do

  • It doesn't support cross form validation. If you want to one form rely on data of another, it can't help you with that. You can access the formik instance of every form and make your own validation, but this library doesn't provide any special utility for that.
  • Nested forms is not supported.

Installation

# using NPM
$ npm i multi-formik-hook
# using Yarn
$ yarn add multi-formik-hook
# using pnpm
$ pnpm i multi-formik-hook

API

useMultiFormik

Returns the following object:

valid: boolean

True if all forms are valid.

dirty: boolean

True if some form is dirty.

map<R>(cb: (item: {key: string, formik: FormikHook}) => R) => R[]

Map over all existing forms and return an array. Where key is a unique key of the form, and formik is the Formik instance.

It also iterates over grouped forms in a flat manner, so you don't need to worry about the nesting. Think about it as someArray.flat(Infinity).map(...).

Example
// Check if some form is currently validating
const instance = useMultiFormik()
const isSomeValidating = instance.map(item => item.formik.isValidating).some(Boolean)

bind(formKey: string): FormikHook

Creates a new Formik hook for the given form key. It dynamically creates a new Formik hook function which you can use inside your form component.

bindGroup(formKey: string, formId: string): FormikHook

Same as bind, but it creates a new Formik hook dedicated to the given form group (basically form array). The formId is a unique key of the form. It's required to preserve the state of the form on dynamic add it or removal.

instances: Record<string, FormikHook>

Object containing all created Formik hooks.

groupInstances: Record<string, Record<string, FormikHook>>

Object containing all created Formik hooks for grouped forms.

submitAll(): Promise<[boolean, result]>

Submits all forms and returns a tuple with valid/invalid validation status and an object containing all form's current values.

getValues(formKey: string): object

Shorthand for getting form values (instance.instances.formKey.values).

getGroupValues(formKey: string, formId: string): object

Shorthand for getting form values of the group by form ID (instance.groupInstances.formKey[formId].values).

reset(formKey?: string): void

Resets all forms to their initial values. If formKey is provided, it resets only the form with the given key.

Usage

Examples

See interactive example on the Stackblitz

Code example

import React, { FC, useState, useCallback } from "react"
import Yup from "yup"
import {
  useMultiFormikHook,
  FormikHook,
} from 'multi-formik-hook'

// Define the type of the form
type PersonalDataFormProps = {
  name: string
  age: string
  email: string
}

// Create a form with the `useFormik` property 
// Use special utility type FormikHook.
const PersonalDataForm: FC<{
  useFormik: FormikHook<PersonalDataFormProps>, initialValues: PersonalDataFormProps 
}> = ({ useFormik, initialValues }) => {
  
  const valudationSchema = useMemo(() => {
    return Yup.object().shape({
      name: Yup.string().required("Name is required"),
      age: Yup.number().required("Age is required"),
      email: Yup.string().email("Email is required").required("Email is required"),
    })
  }, [])
  
  // Use the `useFormik` like a normal Formik hook. All configuration properties are available.
  const formik = useFormik({
    initialValues,
    validationSchema,
  })
  
  return (
    <div>
      <label>
        Name:
        <input
          {...formik.getFieldProps("name")}
        />
        {errors.name && touched.name &&
          <div>{errors.name}</div>}
      </label>
      <label>
        Age:
        <input
          type={'number'}
          {...formik.getFieldProps('age')}
        />
        {errors.age && touched.age &&
          <div>{errors.age}</div>}
      </label>
      <label>
        Email:
        <input
          {...formik.getFieldProps('email')}
        />
        {errors.email && touched.email &&
          <div>{errors.email}</div>}
      </label>
    </div>
  )
}

type PetFormProps = {
  name: string
  breed: 'cat' | 'dog'
}

const PetForm: FC<{
  useFormik: FormikHook<PetFormProps>, initialValues: PetFormProps 
}> = ({ useFormik, initialValues }) => {
  
  const formik = useFormik({
    initialValues,
    validationSchema: Yup.object().shape({
      name: Yup.string().required("Name is required"),
      breed: Yup.mixed().oneOf(["cat", "dog"], "Breed should be cat or dog").required("Breed is required"),
    }),
  })
  
  return (
    <div>
      <label>
        Name:
        <input
          {...formik.getFieldProps("name")}
        />
        {errors.name && touched.name &&
          <div>{errors.name}</div>}
      </label>
      <label>
        Breed:
        <input
          {...formik.getFieldProps('breed')}
        />
        {errors.breed && touched.breed &&
          <div>{errors.breed}</div>}
      </label>
    </div>
  )
}


type MetaFormProps = {
  userData: PersonalDataFormProps
  pets: PetFormProps[]
}

const UserMetaForm:FC = () => {
  const [pets, setPets] = useState<{id: string}[]>([])
  const addPet = () => {
    setPets([...pets, {id: uuid()}])
  }
  const removePet = (id: string) => {
    setPets(pets.filter(pet => pet.id !== id))
  }
  
  const forms = useMultiFormikHook<MetaFormProps>()

  const handleSubmit = useCallback(async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    const [valid, result] = await forms.submitAll()
    if(!valid){
      return
    }

    doSomethingWith(result)
    
  }, [forms])
  
  return (
    <form onSubmit={handleSubmit}>
      <PersonalDataForm
        // provide the `useFormik` hook to the form component
        useFormik={forms.bind('userData')}
      />
      {pets.map(pet => (
        <PetForm
          // for the group forms it is required to set an unique id
          useFormik={forms.bindGroup('pets', pet.id)}
          key={pet.id}
          onRemove={() => removePet(pet.id)}
          initialValues={{
            name: '',
            breed: '',
          }}
        />
      ))}
      <button type={'button'} onClick={addPet}>Add pet</button>
      <button type={'submit'}>Submit</button>
    </form>
  )
}