Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

How to know if an input is required? #712

Closed
jeromeraffin opened this issue Jun 26, 2018 · 16 comments
Closed

How to know if an input is required? #712

jeromeraffin opened this issue Jun 26, 2018 · 16 comments

Comments

@jeromeraffin
Copy link

jeromeraffin commented Jun 26, 2018

I'm using the isValid to check if my input is valid with the schema that I've defined with the help of Yup. And for example the email input is required in my schema. But I'm wondering if it's possible to know from the formik props if an input is required or not in order to add a '*' after the label. Maybe something like a 'isRequired'?

@sshmyg
Copy link

sshmyg commented Jun 26, 2018

I think it's a question for stackoverflow.

@ikashifullah
Copy link

ikashifullah commented Jun 27, 2018

@jeromeraffin, I think you can do that very easily by sending props to your renderInput Component and then you can display it, for example:

<Field name="email" component={renderInput} type="text" label="Tracking ID" placeholder="Tracking ID" requiredStar />

Then in your renderInput component you can do something like this:
<label>{props.label} {props.requiredStar &&<span className="text-danger">*</span>}</label>

So, asterisk is only visible if you have provide it in your props.

I hope that would help you!

@Andreyco Andreyco closed this as completed Jul 8, 2018
@afraser
Copy link

afraser commented Sep 12, 2018

The solution above is essentially hardcoding. It duplicates the definitions you're making in Yup and does not solve the problem of being able to tell if I field is conditionally required:

Consider the following yup form schema for a password field that is only required when type is PASSWORD:

  password: string()
    .when('$type', {
      is: 'PASSWORD',
      then: string().required('Please enter a password'),
      otherwise: string()
    }),

There is no way (that I see) to reuse this logic in Formik, which means you need to duplicate it somewhere. I think the way to solve this would be to provide the validation schema back to the component along with the other formik props. Then it's Yup's job to provide ways of inspecting the schema for this sort of thing, (which it may already do).

@nero2009
Copy link

nero2009 commented Nov 6, 2018

Hi @afraser I don't really understand your approach, please can you share a code snippet.

@afraser
Copy link

afraser commented Nov 6, 2018

@nero2009 I've moved on from this project but basically it's common to do this sort of thing

<InputField
  showRequiredAsterisk={field.isRequired}
  label={field.label}
  error={field.error}
  value={field.value}
  onChange={setField(field.name)}
/>

Formik abstracts away most of these props with magic around the name prop, but the important thing to note is that we want to access whether or not to pass showRequiredAsterisk, but this should be based on the validation schema for the sake of having a single source of truth regarding required fields.

@d-asensio
Copy link

Hi all, I had a similar issue and I made a function to check if a field is required:

import { getIn } from 'formik'

export const isRequiredField = ({ validationSchema }, name) =>
  !!getIn(validationSchema.describe().fields, name).tests.find(
    testName => testName === 'required'
  )

It can be used this way:

import { connect } from 'formik'
import { isRequiredField } from './utils'

const InputText = ({ formik,  ...rest}) => {
  const { handleChange, handleBlur } = formik
  const { name } = rest

  return (
    <div>
      {isRequiredField(formik, name) &&
        <strong>*</strong>
      }
      <input
        {...rest}
        type='text'
        onChange={handleChange}
        onBlur={handleBlur}
      />
    </div>
  )
}

export default connect(InputText)

It is not an elegant soution... but it works! 😄

DISCLAIMER: I did not test properly the code, and I'm not sure if it's a good practice to rely in yup's test name (which is "required") to check this.

I would like to hear what you think about this solution!

@afraser
Copy link

afraser commented Jan 9, 2019

@d-asensio Nice. Better than having to define whether a single field is required in two places.

@Undistraction
Copy link

I've added this as a feature request: #1241

@nbrustein
Copy link
Contributor

nbrustein commented Aug 12, 2019

@d-asensio, the code you provided did not work for me when using when, like this:

Yup.string().when('someOtherField', {
    is: 'specialValue',
    then: Yup.string().required(),
    otherwise: Yup.string().nullable().oneOf([null, undefined], 'Null'),
});

In that case getIn(validationSchema.describe().fields, name).tests returns an empty array, when it seems like it should return a test for required. I tested it, and the required validation is in fact being applied in that case.

(I just noticed that this same comment was made by someone else over on #1241)

@maximilianoforlenza
Copy link

@nbrustein I have the same issue. Did you find a fix for this?

@ShadabFaiz
Copy link

Why is this closed ?

@Charlie91
Copy link

Charlie91 commented Oct 29, 2021

+1, still actual issue.
though using Yup we can work with validationSchema object directly as @d-asensio mentioned

@0livare
Copy link

0livare commented Mar 1, 2022

Hi all, I had a similar issue and I made a function to check if a field is required:

import { getIn } from 'formik'

export const isRequiredField = ({ validationSchema }, name) =>
  !!getIn(validationSchema.describe().fields, name).tests.find(
    testName => testName === 'required'
  )

Doing this is not currently possible unless you use the HoC approach (as shown in @d-asensio's example) because Formik is explicitly not passing down the validationSchema via context.

See #1241

@crhistianramirez
Copy link

Hi all, I had a similar issue and I made a function to check if a field is required:

import { getIn } from 'formik'

export const isRequiredField = ({ validationSchema }, name) =>
  !!getIn(validationSchema.describe().fields, name).tests.find(
    testName => testName === 'required'
  )

Looks like the API has changed slightly, tests is no longer an array of strings but an array of functions. I updated the function to account for this and also added some code to handle nested form objects

import { getIn } from 'formik'

export const isRequiredField = ({ validationSchema }, name) =>
  !!getIn(validationSchema.describe().fields, name.split(".").join(".fields.")).tests.find(
    test=> test.name === 'required'
  )

Here is a slightly more verbose version but a bit easier on the eyes

export const isRequiredField = (validationSchema, name) => {
  const schemaDescription = validationSchema.describe()
  const accessor = name.split(".").join(".fields.")
  const field = getIn(schemaDescription.fields, accessor)
  if (!field) {
    return false
  }
  const isRequired = field.tests.some((test) => test.name === "required")
  return isRequired
}

@BradWells
Copy link

I just got bit with this. Unfortunately I'm using inside of my component instead wrapping with connect(). As @0livare mentions, useFormikContext() does NOT return validationSchema even though the typings say it should.

Is anyone still using this workaround, and does connect() with a HoC still work? Also, does this just work with more complex Yup rules? For example:

Yup.object().shape({
  // ...
  people: Yup.array()
    .of(
      Yup.object().shape({
        person: Yup.number().required('Required'),
        is_present: Yup.boolean().required('Required'),
        notes: Yup.string().optional(),
        is_true_based_on_other_field: Yup.boolean().when('notes', yupRequiredIfTruthy),
        attachments: Yup.mixed().optional(),
      }),
    )
    .required('Required'),
});

@malininss
Copy link

malininss commented Feb 13, 2024

Here is a slightly more verbose version but a bit easier on the eyes

export const isRequiredField = (validationSchema, name) => {
  const schemaDescription = validationSchema.describe()
  const accessor = name.split(".").join(".fields.")
  const field = getIn(schemaDescription.fields, accessor)
  if (!field) {
    return false
  }
  const isRequired = field.tests.some((test) => test.name === "required")
  return isRequired
}

Probably this is a better solution right now:

const checkIsRequired = (
  validationSchema: ObjectSchema<any>,
  fieldName: string
) => {
  const schemaDescription = validationSchema.describe();
  const field = schemaDescription.fields[fieldName];

  return !!(field && 'optional' in field && field.optional === false);
};

But it still doesn't look good

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests