Managing list of form fields with formik through example
Tags: javascript, react, formik, reactforms
Published: 2019-12-31 18:32:15

---
title: Managing list of form fields with formik through example
published: true
description: Build forms in React, without the tears.
tags: javascript, react, formik, react forms
---

Managing sign-in / sign-up form is not that difficult, everyone does it, but what if you've an editable list to manage which has hundreds of items, e.g. a todo app? Worry not [formik](https://jaredpalmer.com/formik/) is to the rescue.

## Why Formik
1. Getting values in and out of form state is very easy and straightforward
2. Handles form submission, validation and error messages very well
3. Keeps form state local
4. *I'm not in mood of crying*
![:sob:](https://media.giphy.com/media/d2lcHJTG5Tscg/giphy.gif)

## What you're going to learn
1. Create editable HTML tag using `contentEditable` prop
2. Formik's `useField` hook
3. `FieldArray` component to manage list

## Getting Started
Let's create a basic component, i.e. `TodoApp.jsx`, for our todo list app:

```jsx
const INITIAL_TODOS = [
  { id: 1, content: "learn react", isCompleted: true },
  { id: 2, content: "learn react hooks", isCompleted: true },
  { id: 3, content: "learn formik", isCompleted: false }
];

const TodoItem = ({ content, isCompleted }) => (
  <div className={["todo-item", isCompleted ? "completed" : ""].join(" ")}>
    <input type="checkbox" checked={isCompleted} />
    <span contentEditable={true} className="todo-text">{content}</span>
  </div>
);

export const TodoApp = () => {
  return INITIAL_TODOS.map(todo => (
    <TodoItem
      key={todo.id}
      content={todo.content}
      isCompleted={todo.isCompleted}
    />
  ));
};

```

We've 3 todo items along with checkboxes and their content, a checkbox shows if a todo item is complete or not.
Everything is same old React except `contentEditable` prop which is doing some magic, right? Well, it basically make content of an HTML tag editable whether it's text or anything else. We'll see it's real usage in next couple of code snippets.
Let's add some basic styling for todo items:

```css
.todo-item {
  display: flex;
  border: 1px dashed #999;
  margin: 5px 0;
  padding: 5px;
}
.todo-item.completed {
  text-decoration: line-through;
  background: #80eec5;
}
.todo-text {
  flex-grow: 1;
  margin-left: 10px;
  min-height: 20px;
  /* Removes outline when using contentEditable */
  outline: none;
  overflow: hidden;
  word-break: break-word;
}
```

## The one with Formik
Run `yarn add formik` or `npm i --save formik` in your project repo.
We're going to wrap our todo items with `Formik`.

```jsx
import { Formik } from "formik";

export const TodoApp = () => (
  <Formik initialValues={{ todos: INITIAL_TODOS }}>
    {formikProps => {
      const { values } = formikProps;

      return values.todos.map((todo, index) => (
        <TodoItem key={todo.id} index={index} />
      ));
    }}
  </Formik>
);
```

Nothing has happened just yet in actual but we've successfully integrated formik with our tiny `TodoApp`.

### The one with useField
We've to change `TodoItem` component now as we're passing `index` of the array in props.

```jsx
import { useField } from "formik";

const TodoItem = ({ index }) => {
  // Binding `isCompleted` using index of todos array
  const [completedField] = useField({ name: `todos[${index}].isCompleted`, type: "checkbox" });
  // Binding `content` using index of todos array
  const [contentField, contentMeta, contentHelpers] = useField(`todos[${index}].content`);
  const onContentInput = e => {
    contentHelpers.setValue(e.currentTarget.innerText);
  };
  const onContentBlur = () => {
    contentHelpers.setTouched(true);
  };

  return (
    <div
      className={["todo-item", completedField.value ? "completed" : ""].join(" ")}
    >
      <input
        type="checkbox"
        name={completedField.name}
        checked={completedField.checked}
        onChange={({ target }) => {
          completedHelpers.setValue(target.checked);
          // As type=checkbox doesn't call onBlur event
          completedHelpers.setTouched(true);
        }}
      />
      <span
        contentEditable={true}
        className={"todo-text"}
        onInput={onContentInput}
        onBlur={onContentBlur}
      >
        {/*
         * We must set html content through `contentMeta.initialValue`,
         * because `contentField.value` will be updated upon `onChange | onInput`
         * resulting in conflicts between states of content. As 1 will be managed by
         * React and other with contentEditable prop.
         */}
        {contentField.value}
      </span>
    </div>
  );
};
```

Custom hooks are now part of `formik >= v2`, `useField` hook returns a 3-tuple (an array with three elements) containing `FieldProps`, `FieldMetaProps` and `FieldHelperProps`. It accepts either a string of a field name or an object as an argument. The object must at least contain a `name` key. You can read more about `useField` [here](https://jaredpalmer.com/formik/docs/api/useField).

### The one with FieldArray

Enough with the static data, let's dig a bit deeper and create an Add button for dynamically creating todo items. To do that we can make use of `FieldArray`. `FieldArray` is a component that helps with common array/list manipulations. You pass it a `name` property with the path to the key within `values` that holds the relevant array, i.e. `todos`. `FieldArray` will then give you access to array helper methods via render props.

**Common array helper methods:**
* `push: (obj: any) => void`: Add a value to the end of an array
* `swap: (indexA: number, indexB: number) => void`: Swap two values in an array
* `move: (from: number, to: number) => void`: Move an element in an array to another index
* `remove<T>(index: number): T | undefined`: Remove an element at an index of an array and return it

To read more about `FieldArray` visit official [documentation](https://jaredpalmer.com/formik/docs/api/fieldarray).

```jsx
import { Form, Formik, FieldArray } from "formik";

export const TodoApp = () => (
  <Formik initialValues={{ todos: [] }}>
    <Form>
      {/* Pass name of the array, i.e. `todos` */}
      <FieldArray name="todos">
        {({ form, ...fieldArrayHelpers }) => {
          const onAddClick = () => {
            fieldArrayHelpers.push({
              id: values.todos.length,
              content: "",
              isCompleted: false
            });
          };

          return (
            <React.Fragment>
              <button onClick={onAddClick}>Add Item</button>
              {form.values.todos.map(({ id }, index) => (
                <TodoItem key={id} index={index} />
              ))}
            </React.Fragment>
          );
        }}
      </FieldArray>
    </Form>
  </Formik>
);
```

There you go, it was that simple, you've a working Todo app.
![:satisfied:](https://media.giphy.com/media/5xtDarqlsEW6F7F14Fq/giphy.gif)
You can add more features like removing the completed items but that's totally up to you.

```jsx
          ...
const onRemoveClick = () => {
  form.setFieldValue(
    "todos",
    form.values.todos.filter(todo => !todo.isCompleted)
  );
};

<button onClick={onRemoveClick}>Remove</button>
          ...

```
I've skipped the validation part in this article as it was pretty simple but it's all implemented in the sandbox embedded below:
{% codesandbox todoapp-hnv5r %}

*Most of the documentation part is taken from formik's [official documentation](https://jaredpalmer.com/formik/) and big thanks to [Jared Palmer](https://twitter.com/jaredpalmer) for all of the efforts.*