Formik: TypeError: Cannot read property 'length' of undefined when using Yup.addMethod()

Created on 21 Sep 2018  路  17Comments  路  Source: formium/formik

I'm using formik with yup and using Yup.addMethod().
I'm using React 16.5
Example:
validationSchema: Yup.object().shape({ file:Yup.addMethod(Yup.string, 'file',(file)=>{ console.log(file); return "This is not a valid file."; }) }),
I'm getting error:
TypeError: Cannot read property 'length' of undefined

Formik User Land

Most helpful comment

For those coming after me, what I realized is that the yup test method actually doesn't accept an arrow function.

Doesn't work:

                confirmPassword: Yup.string()
                .min(6, 'Password must be at least 6 characters')
                .test('passwords-match', 'Password Confirmation must match', value => {
                  if (!value) {
                    return false;
                  }
                  return this.parent.password === value;
                })
                .required('Password Confirmation is required.')

Works:

                confirmPassword: Yup.string()
                .min(6, 'Password must be at least 6 characters')
                .test('passwords-match', 'Password Confirmation must match', function(value) {
                  if (!value) {
                    return false;
                  }
                  return this.parent.password === value;
                })
                .required('Password Confirmation is required.')

All 17 comments

I鈥檓 not sure this is a Formik error. Can you please provide a codesandbox and complete the issue template?

@jaredpalmer Thanks for reply.
Here is my codesandbox URL: https://codesandbox.io/s/wz6y0qwrm8
When you will upload any file this will through the error.
If you will comment the following part you will not see on change error:

file: Yup.addMethod(Yup.array, "file", file => {
      console.log(file);
      return "This is not a valid file.";
    })

I'm also getting this error; I'm using yup, but not using .addMethod.

updated yup from 0.24.1 to latest and now getting this error

@raq929 , @nathanhannig

Make sure the specific property of initialValues you're checking inside the validationSchema is also present inside the validationSchema.

See this answer for an example: https://stackoverflow.com/questions/49394391/conditional-validation-in-yup?rq=1

What @fullmetalsheep wrote helped me in one case. Other case was that my custom string().test() method had an error inside and crashed silently resulting in this message. See inside all your custom test() methods if they do not crash (In my case I didn't handle undefined value passed to it)

Hey @fullmetalsheep I am getting this error while using Formal & Yup in combination.

const schema = yup.object().shape({
  Username: yup.string().required(),
  Email: yup
    .string()
    .email('Invalid Email Address')
    .required(),
  Password: yup
    .string()
    .min(4)
    .max(32)
    .matches('^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$')
    .required(),
  'Confirm Password': yup
    .string()
    .oneOf([yup.ref('Password'), null], "Passwords don't match")
    .required(),
})

const initialValues = {
  Username: '',
  Email: '',
  Password: '',
  'Confirm Password': '',
}

const Field = ({ placeholder, error, ...props }) => (
  <>
    <Text style={styles.space}>{placeholder}</Text>
    <Input {...props} />
    {error && (
      <Text style={[styles.space, error && styles.error]}>{error}</Text>
    )}
  </>
)

function App() {
  const formal = useFormal(initialValues, {
    schema,
    onSubmit: values => {
      Alert.alert(JSON.stringify(values))
    },
  })

  return (
    <SafeAreaView>
      <View style={styles.space}>
        <Text h1>Formal</Text>
        <Field {...formal.getFieldProps('Username')} placeholder="Username" />
        <Field {...formal.getFieldProps('Email')} placeholder="Email" />
        <Field
          {...formal.getFieldProps('Password')}
          placeholder="Password"
          secureTextEntry
        />
        <Field
          {...formal.getFieldProps('Confirm Password')}
          placeholder="Confirm Password"
          secureTextEntry
        />
        <Button
          style={styles.space}
          {...formal.getSubmitButtonProps()}
          title="Submit"
          type="outline"
        />
      </View>
    </SafeAreaView>
  )
}

And the values are present in both my schema & initialValues properties. The weird part is it was working yesterday just fine.

Anything missing?

@deadcoder0904 maybe try replacing 'Confirm Password' with something like confirmPass? I'm currently travelling and have no method of testing code sorry :(

No, it does not work. I was earlier using confirmPass but then changed to Confirm Password when it was working. Thanks though & no worries, safe travels :)

So this happens when you has an error in your custom validation function

In my case the error was

Cannot read property 'replace' of undefined

So yup will throw here https://github.com/jaredpalmer/formik/blob/c5243947fcf21cd873dd86b57689a30ffc46eab8/src/Formik.tsx#L258

if there is real validation errors or if there is an error in the validation function

so this will throw https://github.com/jaredpalmer/formik/blob/c5243947fcf21cd873dd86b57689a30ffc46eab8/src/Formik.tsx#L683

We can check if yupError has inner, and if not, we should show the error to end user

what do you guys think of this solution?

Hey, @sibelius I actually found a workaround solution to it. I removed the .matches('^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$') thing & it worked.

The full working code 馃憠https://github.com/deadcoder0904/expo-formal/blob/master/App.js

For those coming after me, what I realized is that the yup test method actually doesn't accept an arrow function.

Doesn't work:

                confirmPassword: Yup.string()
                .min(6, 'Password must be at least 6 characters')
                .test('passwords-match', 'Password Confirmation must match', value => {
                  if (!value) {
                    return false;
                  }
                  return this.parent.password === value;
                })
                .required('Password Confirmation is required.')

Works:

                confirmPassword: Yup.string()
                .min(6, 'Password must be at least 6 characters')
                .test('passwords-match', 'Password Confirmation must match', function(value) {
                  if (!value) {
                    return false;
                  }
                  return this.parent.password === value;
                })
                .required('Password Confirmation is required.')

@tannerhallman actually, it's not that it doesnt accept arrow functions. It's because you're referencing this inside of your arrow function. this does NOT have the proper Yup context so it errors out.

reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Invoked_through_call_or_apply

@deadcoder0904 the error could be coming from the regex that you used to validate password, instead of using quote around the regex ('{regex}') trying using forward slash (/{regex}/)
before:
Password: yup.string().matches('^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$').required(),

after:
Password: yup.string().matches(/^(?=.*[a-z])(?=.*[A-Z])(?=.*d)[a-zA-Zd]$/).required(),

see the doc for reference: https://github.com/jquense/yup#stringmatchesregex-regex-message-string--function-schema

I had the same problem, but in a test function:

  name: Yup.string()
    .required('Required')
    .test(
      'is-unique',
      'Must be unique(case-insensitive)',
      function test(value) {
        const { existingNames = [] } = this.parent;
        return !existingNames.includes(value.toLowerCase())
      }
    )

to fix this, I had to test for 'value' before calling 'toLowerCase'. I expected this function wouldn't be hit because of the 'required', but I was wrong. This fixed it:

  name: Yup.string()
    .required('Required')
    .test(
      'is-unique',
      'Must be unique(case-insensitive)',
      function test(value) {
        const { existingNames = [] } = this.parent;
        return value && !existingNames.includes(value.toLowerCase())
      }
    )

just adding ? to value or make sure its not undefined

password: yup
    .string()
    .test(
      'password',
      'Password must be at least 8 characters',
      (value: string) => value?.length >= 8
    )
    .required('Password is required!')

Future me has come across a situation similar to this and I have re-read the Yup docs (I know I know, why?!).
Newer versions of Yup have added some clarity. See this section.

Specifically:

test functions are called with a special context, or this value, that exposes some useful metadata and functions. Older versions just expose the this context using function (), not arrow-func, but now it's exposed too as a second argument of the test functions. It's allow you decide which approach you prefer.

Meaning that you can now use the newer function signature of 2 arguments which would involve adding the context to replace this.

  confirmPassword: Yup.string()
            .min(6, 'Password must be at least 6 characters')
            .test('passwords-match', 'Password Confirmation must match', 
             (value, context) => {
                if (!value) {
                  return false;
                }
                return context.parent.password === value;
              })
            .required('Password Confirmation is required.')

Note: I haven't tried this to verify it actually works, but it seems that this explanation was right. Cheers @sgnl

@tannerhallman actually, it's not that it doesnt accept arrow functions. It's because you're referencing this inside of your arrow function. this does NOT have the proper Yup context so it errors out.

reference: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Functions/Arrow_functions#Invoked_through_call_or_apply

Was this page helpful?
0 / 5 - 0 ratings

Related issues

emartini picture emartini  路  3Comments

giulioambrogi picture giulioambrogi  路  3Comments

Jungwoo-An picture Jungwoo-An  路  3Comments

ancashoria picture ancashoria  路  3Comments

najisawas picture najisawas  路  3Comments