I have a UI that I want to respond to the selected values of a formik form. It's a pricing UI where selecting different options results in a different total price. I need to be able to connect a listener for when a formik field has setFieldValue() called on it. I don't see such a function in formik so the way I've gotten around it is to duplicate the form schema as values in the state and every time I call setFieldValue() I also mutate the corresponding state value in the same way.
Can you just move this logic to your render function and determine price/what fields to show based on values?
I need the logic in the render function but I also need it in the handleSubmit() of the formik object so I wrapped the formik object in another component. My code looks like this
const MyForm = props => {
const {
values,
touched,
errors,
handleChange,
handleBlur,
handleSubmit,
getPrice,
} = props;
return (
{getPrice()}
<form onSubmit={handleSubmit}>
<input
type="text"
onChange={handleChange}
onBlur={handleBlur}
value={values.name}
name="name"
/>
{errors.name && touched.name && <div id="feedback">{errors.name}</div>}
<button type="submit">Submit</button>
</form>
);
};
const MyEnhancedForm = withFormik({
mapPropsToValues: () => ({ name: '' }),
handleSubmit: (values, { props, setSubmitting }) => {
// need to use getPrice()
sessionStorage.setItem('price', props.getPrice());
setSubmitting(false);
},
})(MyForm);
class MyEnhancedFormContainer extends React.Component {
constructor(props) {
super(props);
this.state = {
mirroredName: null,
};
}
getPrice = () => {
// does something with this.state.mirroredName
}
render() {
<MyEnhancedForm
getPrice={this.getPrice}
/>
}
}
I recently needed to subscribe on some value. My input was editable with handleChange and also manually with setFieldValue. So to avoid multiple listeners, I just used hook like this:
useEffect(() => {
if (!isSanitized(values.field)) {
setFieldValue('radius', sanitize(values.field));
} else {
action(values.field);
}
}, [values.field]);
Cheers
@lkostrowski In your example, what is action(values.field); doing? I think this may work for my issue... Using recaptcha, if the user waits too long, the value expires. I need to listen for the expiration and simply set the values.recaptcha to blank if it's not a valid token.
It does whatever you like. In my case it's only listening to formik state instead listening to many sources, so I sanitize value if is not. Then, value changes, so hook runs again, but else is running with my action. Don't remember what it was in this example, probably some redux dispatch
Thank you... I was curious if it was dispatching another Formik action. I ended up using your example to handle the recaptcha timeout and it works great.
// if recaptcha times out, an ugly error message is returned
// set the value from null to '' to get the validationSchema message
useEffect(() => {
if (form.values.recaptcha === null) {
form.setFieldValue('recaptcha', '')
}
}, [form.values.recaptcha])
And what if you're working with class components? My initial thought is componentDidUpdate, but the formik values are neither in state, nor props. isSubmitting is close, but not quite what I need.
I came up with this approach:
const HandleFieldChange = connect(
({
name,
formik: {
values: { [name]: value },
},
onChange,
}) => {
useEffect(() => {
onChange(value);
}, [onChange, value]);
return null;
}
);
That you would use inside a form like this:
<HandleFieldChange
name="field-name"
onChange={value => console.log(value)}
/>
Most helpful comment
I recently needed to subscribe on some value. My input was editable with
handleChangeand also manually withsetFieldValue. So to avoid multiple listeners, I just used hook like this:Cheers