DEV Community

Chris
Chris

Posted on • Edited on

Formik and how to create static and dynamic inputs

Formik is a lightweight library that makes building forms in React easier and more streamline. It helps with:

  1. Managing values in state
  2. Handling form submission and resets
  3. Validation and error messages

You can create formik forms by either using useFormik() hook or using the <Formik /> component. For our example, we will be using the component version, which gives us access to various advanced features that can't be utilized using the hook version.

For the purpose of this lesson, we will be covering the following

  • Creating a basic form with an input and selection field
  • Creating a dynamic list of an input and selection field

To start, install formik using your package manager (I used npm), and import the following modules into your React App: import { Formik, Form, Field } from "formik".

To create your first form, we start with the <Formik> <Formik /> component

Next we will pass the following props into this component

function App(){
  const colors = ["Red", "Green", "Blue", "White", "Black", "Yellow", "Orange"];
  const subjects = ["Calculus", "English Lit", "History", "Social Studies", "Physics"];
  const initialValues = {
    name: "",
    color: "",
   }
...
return (
     <Formik
        initialValues={initialValues}
        onSubmit={values => alert(JSON.stringify(values, null, 3))} >


     </ Formik>
)}
Enter fullscreen mode Exit fullscreen mode

Within our app, we are creating an initialValues dictionary and passing that into the initialValues prop. We are also creating a list of colors and subjects to use later in our form. For our onSubmit, we call a function and pass values into an alert. This can also be a callback function written elsewhere in your App.

Next, for a simple input and selection form, we will insert the following within the <Formik> </Formik> components:

<Form >
     <label>Student Name</label>
     <Field name="name" placeholder="Jane Doe" />


     <label>Favorite Color</label>
     <Field name="color" as="select">
          <option label="Make a section" value="" />
          { colors.map(color => <option key={color} label={color} value={color} />) }
     </Field>


     <button type="submit">Submit</button>
</Form>


Enter fullscreen mode Exit fullscreen mode

Now you have a basic form that manages the values of all of your inputs and passes them into a function when you submit it. The first <Field /> manages the data that gets passed into the "name" key of your initialValues. The second <Field > </ Field> manages your selection. The child components are: the first one is an option element with a blank value. The second is a map of the colors list where each color gets assigned its own option value. Last, you have a button whose type is "submit". You do not need to explicitly state what your onClick does as Formik captures this as your onSubmit. However, you can control this action.

Now to use more advanced features of formik, we will convert our code so that the <Formik> component accepts a function as its children (this is formally known as a render prop).

     {formik => (
          <Form >
            <label>Student Name</label>
            <Field name="name" placeholder="Jane Doe" label="Student Name" /> <br /> <br />


            <label>Favorite Color</label>
            <Field name="color" as="select">
              <option label="Make a section" value="" />
              {colors.map(color => <option key={color} label={color} value={color} />)}
            </Field> <br /><br /> <br />

            <button type="submit">Submit</button>
          </Form>
    )}
Enter fullscreen mode Exit fullscreen mode

Using this gives us access to quite a few props that handle submitting your data, handling change, counting submissions, etc.

If you're curious to see all of the props that get passed down, add a {console.log(formik)} to your code within this function.

Image description

To get a full list and understanding of the formik prop library, go to their documentation page.

Now to make our form dynamic.

Include FieldArray within your formik imports. Next, change your initialValues dictionary to include a third key where the values are an array of dictionary objects

  const initialValues = {
    name: "",
    color: "",
    records: [{ subject: "", grade: "" }]
  };
Enter fullscreen mode Exit fullscreen mode

Within the render prop, below the 2nd field and above the submit button, we will add our <FieldArray name="records"> </ FieldArray> Component. We pass "records" as the name of our array, which is the same key name within our initialValues dictionary.

Next you are creating another render prop within the <FieldArray> component.

<FieldArray>
     {({remove, push }) => (
          <div>


          </div>
     )}
</ FieldArray>
Enter fullscreen mode Exit fullscreen mode

For the purposes of our blog, we will only use the remove, and push prop functions.

Now to make this dynamic, we will utilize the following code within the div

{formik.values.records.length > 0 && formik.values.records.map((record, index) => (
    ...
))}
Enter fullscreen mode Exit fullscreen mode

We need to map the records array however, if there are no elements, javascript will give you an error. So to prevent this, you'll need to use a logical AND statement and start it by testing the length of the array as greater than 0. If this evaluates to true, then you can map the array as your 2nd operand. A returned mapped list will result in a truthy result. A logical AND statement where both operands are truthy will result in the value of the last operand returned, which in our case, is the returned map results.

Here is complete code within this block:

<div>
{formik.values.records.length > 0 && formik.values.records.map((record, index) => (
     <div key={index}>


          <label>Grade Value</label>
          <Field name={`records.${index}.grade`} />


          <label>Subject</label>
          <Field name={`records.${index}.subject`} as="select">
               <option label="Make a section" value="" />
               {subjects.map(subject => <option key={subject} label={subject} value={subject} />)}
          </Field> 


          <button type="button" onClick={() => remove(index)}>Remove</button>
     </div>
))}
<button type="button" onClick={() => push({ grade: "", subject: "" })}>Add More</button>
</ div>
Enter fullscreen mode Exit fullscreen mode

This is similar to our first simple form example except the following important details:

  • for both field names, it needs to be dynamic. The name is the records array, the index number, and the key you're accessing.
  • A button is within the map, which the purpose is to use the remove prop. map allows you to access the element's index as the second argument. So passing this into the remove function removes the specific element within the array
  • A button is outside of map but within the our fieldArray render prop. This button adds to the array by pushing an element that contains keys to blank values.

When you submit your form now, you can see the values of your static list and also the values of your dynamic list.

I hope this has been a helpful posting. I created the above form with some minor formatting as a codesandbox. Check it out below

Thanks for reading and happy coding!

Top comments (0)