form icon indicating copy to clipboard operation
form copied to clipboard

canSubmit in form is true even if some fields are unvalid

Open arnars opened this issue 5 years ago • 7 comments

In your own example: https://codesandbox.io/s/react-form-custom-select-multi-select-inputs-6962t

The canSubmit is true even before the two colors in the multiselect have been set. As long as the single select have been set, it's all good.

Is this expected?

arnars avatar Nov 19 '19 10:11 arnars

As of right now, this is by design. I’ll look into the why ASAP and how we could make it better.

Tanner Linsley On Nov 19, 2019, 3:23 AM -0700, Anders Hallundbæk [email protected], wrote:

In your own example: https://codesandbox.io/s/react-form-custom-select-multi-select-inputs-6962t The canSubmit is true even before the two colors in the multiselect have been set. As long as the single select have been set, it's all good. Is this expected? — You are receiving this because you are subscribed to this thread. Reply to this email directly, view it on GitHub, or unsubscribe.

tannerlinsley avatar Nov 20 '19 14:11 tannerlinsley

This is an issue for me as well, this needs to be fixed ASAP. Thanks.

AndriiUhryn avatar Dec 25 '19 10:12 AndriiUhryn

Any fix for this?? My buttons are all active even if an error occurred after validation, not healthy. Please fix this.

AndriiUhryn avatar Jan 13 '20 14:01 AndriiUhryn

Hi, experiencing issues with this as well. I was expecting isValid to remain false until all fields were valid (using fields with validatePristine enabled). I noticed it seems to return true just on the 2nd render. All other renders receive the expected value (false).

Edit: I took a closer look... this is how it behaves:

  1. field is validating
  2. field is done validating and no error is set, but it should be! Form now says its valid: !fieldsAreValidating && fieldsAreValid && !meta.error
  3. field now shows an error

androsj avatar Jul 15 '20 00:07 androsj

As from today, it's still an issue. Essentially canSubmit lies 🤥 . I have resolved from my side by doing like this 1️⃣ :

<div className="mt-5 place-self-center md:col-span-2">
          <formAPI.Subscribe
            selector={(state) => state}
            children={(state) => (
              <SubmitButton isValid={validateFieldMeta(state.fieldMeta)} />
            )}
          />

And, here is my utility function to do the full validation before 'activating' SubmitButton:

import { type FieldMeta } from '@tanstack/react-form';

export function validateFieldMeta(
  fieldMeta: Record<string, FieldMeta>,
): boolean {
  return Object.values(fieldMeta).every(({ isTouched, touchedErrors }) => {
    return isTouched && touchedErrors.length === 0;
  });
}

And, some tests ✅ for the same:

describe('validateFieldMeta', () => {
  it('returns false if all fields are not touched', () => {
    const input = {
      name: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      email: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      help: {
        isValidating: false,
        isTouched: false,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
    };

    const expected = false;

    const actual = validateFieldMeta(input);

    expect(actual).toBe(expected);
  });

  it('returns false if all fields are touched but at least one has errors', () => {
    const input = {
      name: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      email: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      help: {
        isValidating: false,
        isTouched: true,
        touchedErrors: ['Some error'], // Field with error
        errors: [],
        errorMap: {},
      },
    };

    const expected = false;
    const actual = validateFieldMeta(input);
    expect(actual).toBe(expected);
  });

  it('returns true if all fields are touched and have no errors', () => {
    const input = {
      name: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      email: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
      help: {
        isValidating: false,
        isTouched: true,
        touchedErrors: [],
        errors: [],
        errorMap: {},
      },
    };

    const expected = true;
    const actual = validateFieldMeta(input);
    expect(actual).toBe(expected);
  });
});

Only minor issue now is that whenever we use the browser's autocomplete features, without actually clickng 🐭 and typing ⌨️ it doesn't register as being isTouched.

manavm1990 avatar Dec 12 '23 16:12 manavm1990

When first rendered, the onUpdate function is called and even if the default state canSubmit is set to false, the statement (image below) is always true and sets canSubmit to true : image

t-rosa avatar Mar 27 '24 08:03 t-rosa

@t-rosa @manavm1990 please open a new issue with a StackBlitz link or similar. This ticket is specific for the legacy codebase, not the new codebase you're reporting an issue on

crutchcorn avatar Mar 27 '24 18:03 crutchcorn