I've built this sandbox React application that uses Formik forms.
I want to dispatch a Redux action if the email field validation fires and is valid. I want to dispatch the action to do some lookup from the users email. This action will update redux state and populate some other field on my form.
So this dispatch would happen if the email field was valid. The other fields could be invalid and I haven't submitted the form yet.
How do I do that? I can't find any documentation on this!
Thanks!
I've built this sandbox React application that uses Formik forms.
I want to dispatch a Redux action if the email field validation fires and is valid. I want to dispatch the action to do some lookup from the users email. This action will update redux state and populate some other field on my form.
So this dispatch would happen if the email field was valid. The other fields could be invalid and I haven't submitted the form yet.
How do I do that? I can't find any documentation on this!
https://codesandbox.io/s/x32yoo242q
Thanks!
You can create a reusable custom ponent
that calls a secondary action on input
updates. In this case, it'll dispatch an action
(redux action) if the input is valid and has a value, and/or optionally dispatch a secondary action if invalid.
Update: Utilize ponentDidUpdate
. It's less verbose and requires less manual field level handling. In addition, you should also debounce
the function to avoid a lot of redux action triggers.
Working example (email):
Working example (Material UI Select):
ponents/Input (custom ponent -- ponentDidUpdate
controls whether or not to call validateField
)
import React, { Fragment, PureComponent } from "react";
class Input extends PureComponent {
ponentDidUpdate = prevProps => {
const { errors, value } = this.props;
if (errors !== prevProps.errors || value !== prevProps.value) {
this.props.validateField({ errors, value });
}
};
render = () => {
const { errors, label, name, touched, validateField, ...rest } = this.props;
return (
<Fragment>
<label htmlFor={name} style={{ display: "block" }}>
{label}
</label>
<input {...rest} name={name} />
{errors && touched && (
<div style={{ color: "red", marginTop: ".5rem" }}>{errors}</div>
)}
</Fragment>
);
};
}
export default Input;
containers/Form (validateField
controls the redux action)
import debounce from "lodash/debounce";
import React from "react";
import { withFormik } from "formik";
import Input from "../../ponents/Input";
import DisplayFormikState from "../../ponents/DisplayFormState";
import { resetMessage, setMessage } from "../../actions/message";
import store from "../../store";
import * as Yup from "yup";
const formikEnhancer = withFormik({
validationSchema: Yup.object().shape({
email: Yup.string()
.email("Invalid email address")
.required("Email is required!")
}),
mapPropsToValues: props => ({
email: ""
}),
handleSubmit: (values, { setSubmitting }) => {
const payload = {
...values
};
setTimeout(() => {
alert(JSON.stringify(payload, null, 2));
setSubmitting(false);
}, 1000);
},
displayName: "MyForm"
});
const handleFormReset = handleReset => {
store.dispatch(resetMessage());
handleReset();
};
const validateField = debounce(
({ errors, value }) =>
!errors && value
? store.dispatch(setMessage())
: store.dispatch(resetMessage()),
500
);
const MyForm = props => {
const {
values,
touched,
dirty,
errors,
handleBlur,
handleChange,
handleReset,
handleSubmit,
isSubmitting
} = props;
return (
<form onSubmit={handleSubmit}>
<Input
name="email"
label="Email"
type="email"
placeholder="Enter an email address."
errors={errors.email}
value={values.email}
touched={touched.email}
onChange={handleChange}
onBlur={handleBlur}
validateField={validateField}
/>
<button
type="button"
className="outline"
onClick={() => handleFormReset(handleReset)}
disabled={!dirty || isSubmitting}
>
Reset
</button>
<button type="submit" disabled={isSubmitting}>
Submit
</button>
<DisplayFormikState {...props} />
</form>
);
};
export default formikEnhancer(MyForm);
The easiest and most straightforward way to achieve what you want is by using the onBlur
handler of your email input. You can extend the default handler by doing the following:
<input
id="email"
placeholder="Enter your email"
type="email"
value={values.email}
onChange={handleChange}
onBlur={e => {
handleBlur(e);
if (!errors.email) {
console.log("dispatch: ", e.currentTarget.value);
}
}}
/>
By also calling the Formik's handleBlur
first you keep the default validation behavior, while also adding your own afterwards. Your form is validating onChange
as well as onBlur
so errors.email
should always give an accurate representation of validity for the email input, which we can use to dispatch if the email is valid on blur (In my example I'm just logging something to the console to illustrate). Check out the sandbox to see it in action: