formik-mui icon indicating copy to clipboard operation
formik-mui copied to clipboard

Ability to disable default blur handler

Open kendallroth opened this issue 2 years ago • 6 comments

Summary 💡

Not all use cases may wish to have the Formik onBlur function called to touch fields when blurring. Ideally there would be a way to disable this behaviour (see motiviation section). I do not know whether it would be possible for users to disable this globally, or whether it might just need to respect the Formik validateOnBlur maybe, or another option all together?

Obviously this can be disabled "manually" by passing a custom no-op onBlur function; however, this would require passing a no-op to every textfield (or general MUI form component) or wrapping in a custom component (not awful, but not ideal).

Motivation 🔦

While common, calling the blur function automatically results in fields being "touched" on blur, while some people may wish to only have them touched on submit. For example, it may be desired to not see errors until after the initial submit, which is not possible currently (as fields are touched on blur).

kendallroth avatar Apr 29 '22 19:04 kendallroth

Hi @kendallroth That functionality is actually built into formik.

https://github.com/jaredpalmer/formik/blob/b9cc2536a1edb9f2d69c4cd20ecf4fa0f8059ade/packages/formik/src/Formik.tsx#L691

I don't think this library can actually do anything about it.

cliedeman avatar Apr 29 '22 20:04 cliedeman

Yeah, I am definitely aware that it is built in to Formik (not a problem). What I am referring to specifically though is that it would be nice if there was a way to disable actually calling the onBlur function in this library.

For example, in the Formik docs mentioning MUI, the onBlur handler has been left entirely out (resulting in fields only being touched on submit). However, there is no way to disable the onBlur handler from being called in this library except by passing a custom onBlur handler to all fields (not ideal, although could wrap and copy prop definitions, etc...).

I was hoping there may be a way to maybe skip the default blur handler if validateOnBlur was false (from Formik), but this certainly might not be the most intuitive approach 😄. Otherwise, if there would be a way to disable globally (can think in Vue implementation, but not React at the moment 🤦), that would also work?

kendallroth avatar Apr 29 '22 20:04 kendallroth

As a temporary (and awful 😉) workaround, I have used patch-package to persist manual changes to the dist/esm files.

Also, it looks like Select already has a no-op onBlur function instead of calling Formik, and a few others may as well?

P.S. Only linking this here for others who may wish to use a similar hacky approach vs passing no-ops everywhere or wrapping components (basically just laziness 🤦). It should not be considered a library recommendation at all!

diff --git a/node_modules/formik-mui/dist/esm/TextField.js b/node_modules/formik-mui/dist/esm/TextField.js
index 8c6a53c..a078ff7 100644
--- a/node_modules/formik-mui/dist/esm/TextField.js
+++ b/node_modules/formik-mui/dist/esm/TextField.js
@@ -7,9 +7,7 @@ function fieldToTextField(_a) {
     var disabled = _a.disabled, _b = _a.field, fieldOnBlur = _b.onBlur, field = __rest(_b, ["onBlur"]), _c = _a.form, isSubmitting = _c.isSubmitting, touched = _c.touched, errors = _c.errors, onBlur = _a.onBlur, helperText = _a.helperText, props = __rest(_a, ["disabled", "field", "form", "onBlur", "helperText"]);
     var fieldError = getIn(errors, field.name);
     var showError = getIn(touched, field.name) && !!fieldError;
-    return __assign(__assign({ error: showError, helperText: showError ? fieldError : helperText, disabled: disabled !== null && disabled !== void 0 ? disabled : isSubmitting, onBlur: onBlur !== null && onBlur !== void 0 ? onBlur : function (e) {
-            fieldOnBlur(e !== null && e !== void 0 ? e : field.name);
-        } }, field), props);
+    return __assign(__assign({ error: showError, helperText: showError ? fieldError : helperText, disabled: disabled !== null && disabled !== void 0 ? disabled : isSubmitting, onBlur: onBlur }, field), props);
 }
 function TextField(_a) {
     var children = _a.children, props = __rest(_a, ["children"]);
diff --git a/node_modules/formik-mui-lab/dist/esm/DatePicker.js b/node_modules/formik-mui-lab/dist/esm/DatePicker.js
index 49c5bd0..dd46e39 100644
--- a/node_modules/formik-mui-lab/dist/esm/DatePicker.js
+++ b/node_modules/formik-mui-lab/dist/esm/DatePicker.js
@@ -9,10 +9,7 @@ function fieldToDatePicker(_a) {
     var _b = _a.field; _b.onChange; var field = __rest(_b, ["onChange"]), _c = _a.form, isSubmitting = _c.isSubmitting, touched = _c.touched, errors = _c.errors, setFieldValue = _c.setFieldValue, setFieldError = _c.setFieldError, setFieldTouched = _c.setFieldTouched, _d = _a.textField, _e = _d === void 0 ? {} : _d, helperText = _e.helperText, onBlur = _e.onBlur, textField = __rest(_e, ["helperText", "onBlur"]), disabled = _a.disabled, label = _a.label, onChange = _a.onChange, onError = _a.onError, renderInput = _a.renderInput, props = __rest(_a, ["field", "form", "textField", "disabled", "label", "onChange", "onError", "renderInput"]);
     var fieldError = getIn(errors, field.name);
     var showError = getIn(touched, field.name) && !!fieldError;
-    return __assign(__assign({ renderInput: renderInput !== null && renderInput !== void 0 ? renderInput : (function (params) { return (React.createElement(TextField, __assign({}, params, { error: showError, helperText: showError ? fieldError : helperText, label: label, onBlur: onBlur !== null && onBlur !== void 0 ? onBlur : function () {
-                setFieldTouched(field.name, true, true);
-            } }, textField))); }), disabled: disabled !== null && disabled !== void 0 ? disabled : isSubmitting, onChange: onChange !== null && onChange !== void 0 ? onChange : function (date) {
-            setFieldTouched(field.name, true, false);
+    return __assign(__assign({ renderInput: renderInput !== null && renderInput !== void 0 ? renderInput : (function (params) { return (React.createElement(TextField, __assign({}, params, { error: showError, helperText: showError ? fieldError : helperText, label: label, onBlur: onBlur }, textField))); }), disabled: disabled !== null && disabled !== void 0 ? disabled : isSubmitting, onChange: onChange !== null && onChange !== void 0 ? onChange : function (date) {
             setFieldValue(field.name, date, true);
         }, onError: onError !== null && onError !== void 0 ? onError : createErrorHandler(fieldError, field.name, setFieldError) }, field), props);
 }

kendallroth avatar Apr 29 '22 20:04 kendallroth

I have managed to avoid this so far but I thought we might eventually need it.

We could create a Context that allows users to customize beaviour

interface FormikMuiContextType {
  disableSubmitLoading: boolean,
  disableDefaultBlurHandler: boolean,
}

const FormikMuiContext = React.createContext({
  // defaults
  disableSubmitLoading: false,
  disableDefaultBlurHandler: false,
})

Then users can customize behaviour globaly or per form or component

(Select is probably an oversight)

cliedeman avatar Apr 29 '22 20:04 cliedeman

Gotcha, yeah, something like that could be nice (if it didn't introduce too much overhead).

On the other hand, I honestly just usually wrap Formik components myself (as I only use a few of them) and apply this behaviour in that manner. This library was really easy to get up and running and saved that time though (thanks for putting it together 🎉)! And I could just wrap the component as well, but that felt a little extra/unnecessary if there was something else (although using patch-package feels like a step down honestly 🤣).

P.S. If the no-op on Select was an oversight, it certainly made my life a bit easier (one less component to hack). Given that select fields work a bit differently than text fields, might it have been intentional? On the other hand, the date picker does set touched directly as well, so maybe not too different than text fields?

kendallroth avatar Apr 29 '22 20:04 kendallroth

I actually use a mixture of both. I have several projects using it and some of the more complicated components (Autocomplete) is really tricky to do right. But yes the core goal is to get people going quickly

cliedeman avatar Apr 29 '22 20:04 cliedeman