Formik: TypeError: `array.splice is not a function` with non-array errors

Created on 5 Dec 2018  路  23Comments  路  Source: formium/formik

馃悰 Bug report

Current Behavior

  • Create a form with a FieldArray.
  • setErrors to be of the form {"0": ['invalid']} (note that we're not using an array here).
  • When you remove the first item in the field array (using arrayHelpers.remove, it crashes with TypeError: a.splice is not a function

Expected behavior

No crash, should just work. Note that while I was testing, it seems it was working fine with 1.2.0.

Reproducible example

https://codesandbox.io/s/mjoknk1q2y

// Helper styles for demo
import "./helper.css";
import { MoreResources, DisplayFormikState } from "./helper";

import React from "react";
import { render } from "react-dom";
import { Formik, Form, FieldArray, Field, getIn } from "formik";
import * as Yup from "yup";

// To reproduce:
// 1. Click submit
// 2. Try to remove the first item
// 3. Observe the crash `array.slice is not a function`

const App = () => (
  <div>
    <h1>Friend List</h1>
    <Formik
      initialValues={{ friends: [{ name: "jared" }] }}
      onSubmit={(values, actions) => {
        // HERE, note that we set the error using an object - not an array.
        actions.setErrors({ friends: { "0": { name: ["invalid"] } } });
      }}
      render={({ values }) => (
        <Form>
          <FieldArray
            name="friends"
            render={arrayHelpers => (
              <div>
                {values.friends && values.friends.length > 0 ? (
                  values.friends.map((friend, index) => (
                    <div key={index}>
                      <Field name={`friends.${index}.name`}>
                        {({ field, form }) => {
                          const error = getIn(form.errors, field.name);
                          console.log(error);
                          console.log(field);
                          console.log(form.touched[field.name]);
                          return (
                            <div>
                              <input
                                type="text"
                                {...field}
                                placeholder="First Name"
                              />
                              {error && <div className="error">{error}</div>}
                            </div>
                          );
                        }}
                      </Field>
                      <button
                        type="button"
                        onClick={() => arrayHelpers.remove(index)} // remove a friend from the list
                      >
                        -
                      </button>
                      <button
                        type="button"
                        onClick={() => arrayHelpers.insert(index, "")} // insert an empty string at a position
                      >
                        +
                      </button>
                    </div>
                  ))
                ) : (
                  <button type="button" onClick={() => arrayHelpers.push("")}>
                    {/* show this when user has removed all friends from the list */}
                    Add a friend
                  </button>
                )}
                <div>
                  <button type="submit">Submit</button>
                </div>
              </div>
            )}
          />
        </Form>
      )}
    />
  </div>
);

render(<App />, document.getElementById("root"));

Suggested solution(s)

In my case, the back-end returns nested object errors using an object with numerical keys. This makes sense, because it allows it to report an error for the 125th object by just returning {friends: {124: ["invalid"]}}.

This looks like a regression - formik should allow that too.

Your environment

| Software | Version(s) |
| ---------------- | ---------- |
| Formik | 1.3.2
| React | 16.6.3
| TypeScript | N/A
| Browser | N/A
| npm/Yarn | N/A
| Operating System | N/A

Most helpful comment

I'm also hitting this error. In my case, it's only an issue after doing setFieldTouched(name, true) with a FieldArray.

All 23 comments

The problem is in https://github.com/jaredpalmer/formik/blob/master/src/FieldArray.tsx#L197

remove<T>(index: number): T {
    // We need to make sure we also remove relevant pieces of `touched` and `errors`
    let result: any;
    this.updateArrayField(
      // so this gets call 3 times
      (array?: any[]) => {
        const copy = array ? [...array] : [];
        if (!result) {
          result = copy[index];
        }
        if (isFunction(copy.splice)) {
          copy.splice(index, 1);
        }
        return copy;
      },
      true,
      true
    );

    return result;
  }

which compiles to:

FieldArrayInner.prototype.remove = function (index) {
        var result;
        this.updateArrayField(function (array) {
            var copy = array ? array.slice() : []; // ERROR HERE
            if (!result) {
                result = copy[index];
            }
            if (isFunction(copy.splice)) {
                copy.splice(index, 1);
            }
            return copy;
        }, true, true);
        return result;
    };

which assumes that the object is an array.

I'd be happy to take a stab at fixing this. I doubt I'm the only one having this issue, because this (object, not array) format is what is returned by marshmallow, a pretty common Python validation library, and because setIn and getIn supports it as well.

Checking the test file, there's no test for a list of objects (as opposed to just a simple array of strings), happy to start there as well.

I did a proof-of-concept fix in #1178 I'm not entirely happy with, @jaredpalmer would you mind having a look?

I'm also hitting this error. @jaredpalmer - is there any chance to merge @charlax changes?

Also running into this error when trying to set errors as an object. (i.e. {"0": {name: "Error"} }

@jaredpalmer gentle ping here :)

I'm also hitting this error. In my case, it's only an issue after doing setFieldTouched(name, true) with a FieldArray.

@jaredpalmer any feedback from your side? It's February 2019 already and Formik is 1.5.1, but the issue is still there and it's very annoying. The PR is suggested, so maybe take your time and look into this.

Ran into this issue as well. As a workaround, I'm calling:setFieldError(`${name}`, []) to remove the error before calling arrayHelpers.remove(index)

I ended up not using array helpers at all. You can get away easily just using regular setFieldValue function.

Ran into this issue too. Any hope it gets fixed?

1.) Got this issue too.
2.) Why there no hook version of FieldArray like useFieldArray?

Getting this too.

Me too when i am trying setFieldTouched my fieldArray

i got a similar error, but it was because i forgot to set the name in the field array

Receiving this when using setFieldTouched within <FieldArray />

Having same issue with arrayHelpers.remove(index). Switched to setFieldValue as a workaround

Same issue here with arrayHelpers.remove(index)

Same error here with arrayHelpers.insert(index, value) when using Yup as a validator with Yup.array().min(1), which gives an error string, and then I get a copy.splice() is not a function, because copy is the error string coming from Yup

@giubatt Yeah! I got that error too. Any update? This issue is quite a long time now.

Since this (https://github.com/jaredpalmer/formik/pull/1178) seems to have been abandoned, I created PR that hopefully fixes this for all arrayHelper functions: https://github.com/jaredpalmer/formik/pull/1830.

I'm having the same issue. Removing the validation made it work, but I really need validation.

I'm also hitting this issue. Is there a resolution?

Was this page helpful?
0 / 5 - 0 ratings

Related issues

najisawas picture najisawas  路  3Comments

pmonty picture pmonty  路  3Comments

jaredpalmer picture jaredpalmer  路  3Comments

outaTiME picture outaTiME  路  3Comments

jordantrainor picture jordantrainor  路  3Comments