jaredpalmer / formik

Build forms in React, without the tears 😭
https://formik.org
Apache License 2.0
33.87k stars 2.79k forks source link

Would it make sense to add `disabled` and `setDisabled` to the API? #2454

Open battaglr opened 4 years ago

battaglr commented 4 years ago

🚀 Feature request

Current Behavior

Currently using setFieldValue or setFieldTouched we can achieve a high level of control in those situations where the default behavior falls sort. And that's awesome! BUT, I haven't found a way to programmatically set the disabled property for each field.

Desired Behavior

I would like to be able to handle the disabled property for each field using Formik itself. An example would be dependant inputs, where I need to disable one or more inputs depending on another.

Suggested Solution

I would crate a disabled and setFieldDisabled (and I think a setDisabled for consistency and more flexibility) that would work similarly to touched and setFieldTouched or errors and setFieldError.

Who does this impact? Who is this for?

Advanced or custom use cases.

Describe alternatives you've considered

I couldn't find a way to enable this functionality directly on Formik.

Additional context

I tried to find an issue similar to this but didn't found anything. Sorry if it was already discussed. However, I found these issues that could be related:

mdecurtins commented 4 years ago

This would be super helpful. +1

boyur commented 4 years ago

Yes, I really lacks too. +1

flora8984461 commented 3 years ago

Like it +1

johnrom commented 3 years ago

Can someone provide a specific use case? I would generally do this declaratively like:

<Formik {...formikProps}>
  ({ values }) => { // or useFormikContext in a child component
    <Field type="checkbox" name="hasNotes" value="yes" />
    <Field name="notes" disabled={!values.hasNotes} />
  }
</Formik>

Whether a field is disabled or not generally has nothing to do with whether that field is valid. Disabled fields should still be in a "valid" state when a field is submitted. I might use a prop like required={!disabled} to make the validation depend on disabled-ness.

flora8984461 commented 3 years ago

For me, I'm building a dropdown, after selecting existing options, it prefills other input data, it's typically for address. When selecting an organization, it prefills the address info and makes address input disabled, only prefilled with the data, not allowing users to edit.

But actually, I can make it work to pass another prop {dsiabled?} down to the input. But I was thinking, when I prefill the data, I use setFieldValue, is there anything like setFielddisabled() at the same time? Instead of passing the props.

I'm not sure what are others' use cases. Mine seems pretty simple.

Thanks!

Can someone provide a specific use case? I would generally do this declaratively like:

<Formik {...formikProps}>
  ({ values }) => { // or useFormikContext in a child component
    <Field type="checkbox" name="hasNotes" value="yes" />
    <Field name="notes" disabled={!values.hasNotes} />
  }
</Formik>

Whether a field is disabled or not generally has nothing to do with whether that field is valid. Disabled fields should still be in a "valid" state when a field is submitted. I might use a prop like required={!disabled} to make the validation depend on disabled-ness.

flora8984461 commented 3 years ago

Another use case I think is the validation remains active when disabling a field, as mentioned in the above issues.

johnrom commented 3 years ago

Validation should always remain active whether a field is disabled or not. Whether your validation method changes based on disabled-ness can be handled in userland. A field still has a value if it's disabled, and should be in a valid state when the form is submitted. @flora8984461 passing disabled in the way you describe above is exactly how I'd handle it. In my opinion a field should not be concerned with whether another field is disabled or not -- it is not that field's responsibility, but the Form's.

If you really want to disable validation on a disabled field, you should remove the Field or useField component from the DOM and replace it with one not bound to Formik, like a simple <input disabled />. Field-level validation will be removed once FIeld or useField unmounts.

johnrom commented 3 years ago

I circled back to this and I slapped my face when I saw what I wrote above. Yes, Formik still has the value in its values when a field is disabled -- but submitForm shouldn't receive that value, because plain HTML would not submit this value. I think if we were to add something like this, disabled fields should be removed from values on submission.

Note that a disabled field still has a value, so Formik should still store that state in values, but it should be stripped out during submission, and the full values object should still be accessible from somewhere else in the event the developer wants to search for that disabled value (much like someone using jQuery to get $('#disabled-field').value manually).

scottBowles commented 1 year ago

Along similar lines, it would be very nice to be able to hand <Formik /> something like a mapStateToProps function that takes form state and returns an object mapping form keys to values that get merged in with field props. (This could, alternatively, map form state to disabled values specifically.)

My current use case involves having an array of form keys at the top level which I know should be disabled (given information about the user and other entities). As it stands I have to pass a readonlyFields prop down, or set up and consume my own separate context. But given that this is very much a form concern, it would be great if I could instead handle this at the top level and have Formik take care of the rest.

This way I could do something like the following, and have disabled state taken care of, with the d

const MyForm = ({ user }) => {
    const mapFormStateToProps = (formProps) => ({
        title: { disabled: !user.isStaff },
        firstName: { disabled: false },
        ...
    });

    return <Formik
        onSubmit={handleSubmit}
        initialValues={initialValues}
        validationSchema={validationSchema}
        mapFormStateToProps={mapFormStateToProps)
    >
    ...
    </Formik>
}