stop handleBlur from doing anything

Viewed 1970

In my Formik form, when I click on the search button, a list of items is rendered (UsersFoundList). This list is outside the form and each item in the list has a button of its own.

When I click on any of those buttons from the list for the first time, they don't work. Instead, the handleBlur of my form is triggered and since the input field is empty, an error messages starts showing under the input field. So, the button doesn't do what it's supposed to do in the first attempt. I tested the handleBlur by writing a console log into it.

This error should only show up when I am trying to submit the form again. Not when I click on anything else.

const [showFlatList, setShowFlatList] = useState(false);

    const handleSubmitForm = (
    values: FormValues,
    helpers: FormikHelpers<FormValues>,
  ) => {
    console.log('hello', values.input)
    setShowFlatList(true);
    helpers.resetForm();
  };
  return (
    <View style={styles.container}>
      <View >
          <Formik
            initialValues={initialValues}
            onSubmit={handleSubmitForm}
            validationSchema={addRelationValidationSchema}>
            {({ handleChange, handleBlur, handleSubmit, values }) => (
              <View style={styles.searchFieldContainer}>
                <View style={styles.form}>
                  <FieldInput
                    handleChange={handleChange}
                    handleBlur={handleBlur}
                    value={values.input}
                    fieldType="input"
                    icon="user"
                    placeholderText="E-Mail oder Telefonnummer oder Name"
                  />
                  <ErrorMessage
                    name="input"
                    render={(msg) => <ErrorText errorMessage={msg} />}
                  />
                </View>
                <View style={styles.buttonContainer}>
                  <NewButton buttonText="Suchen" onPress={handleSubmit} />
                </View>
              </View>
            )}
          </Formik>
        </View>
                <View style={styles.listHolder}>
          {showFlatList && (
            <UsersFoundList/>
          )}
        </View>
    </View>
  );

If you run the codesandbox on a phone, you can see the problem:

https://snack.expo.io/@nhammad/jealous-beef-jerky

I need to stop the handleBlur from doing anything. If I click on the a button from the list, it should work on the very first attempt instead of triggering the handleBlur. The problem still exists even if I add validateOnBlur=false. In this case, the error message doesn't show up but the button still doesn't work on the first attempt.

The button that I click has a separate function. and this button is outside the form so it should do what it's originally supposed to do instead of doing anything in the onBlur(in the sandbox: it's just printing).

2 Answers

onBlur is the default behavior using Formik. It will trigger if you were focusing a field, no matter if you click on something outside the form or not.

And onBlur will trigger before any onClick event.

There are a couple of workarounds though.

  1. Use onMouseDown (& onKeyDown) instead of onClick

Basically, onMouseDown will be called before onBlur. See an example implementation here: https://github.com/formium/formik/issues/2062

<a
    href="#back-to-previous"
    onMouseDown={onClick}
    onKeyUp={(e) => {
      e.key === 'Enter' && onClick(e);
    }}
>

onKeyDown is there for accesibility, and you should check that is the Enter key the one being pressed, not just any key.

  1. Override onBlur and call formik onBlur conditionally

This may sound hacky, but it could fit some scenarios.

Example:

onBlur={(e) => {
  const wasLinkClicked = e.relatedTarget && e.relatedTarget.tagName === 'A';
   if (!wasLinkClicked) {
      handleBlur(e);
  }
}}

That code checks wether you are clicking a link. In your case, you could write a different logic: "was the clicked button inside the form?".

In your FieldInput definition you are calling the function when onBlur every render.

To prevent that from happening you can use an arrow function, which React will render only on blur

onBlur={(fieldType) => handleBlur(fieldType)}

You can find a more extensive explanation in the React.js Docs:

https://reactjs.org/docs/faq-functions.html

Related