Ability to disable default blur handler
kendallroth opened this issue Β· 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).
Hi @kendallroth
That functionality is actually built into formik.
I don't think this library can actually do anything about it.
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?
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);
}
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)
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?
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