I had a question about triggering an API call that updates a read-only field within the form after a set of particular fields are updated. For example I have textA and textB. if I change textA or textB I want to call an API to calculate a new value for textC. Right now I have textA and textB set as Field components and I have attached an onChange handler to each of them. When one changes, I dispatch (with redux+redux-thunk) an action called fetchNewTextC and I pass the thunk setFieldValue as well as values from the formik bag. Inside the thunk (fetchNewTextC) I use values to grab the current value of textA and textB since they are required in my request to fetch the new value for textC. I send the request, get a response back and use setFieldValue(textCFieldName, response.value) within my thunk (fetchNewTextC) to set the value within Formik's state. Is this an acceptable/common pattern for using the library? It feels weird passing setFieldValue and values references down to my thunk, but it's the only way I can make sense of it. Hopefully I explained my scenario well enough.
This is actually spot on for how you should do it with Redux. If you remove the Redux jargon from your solution above, then all you are really doing is passing some setters and state to pure functions that live outside your form. This is perfect and exactly how you should do it.
In addition, depending on your app, you may not need/want Redux at all here. If your async call fetchNewTextC is heavily related to textC (which it seems like it is), then you may find it more maintainable to collocate that data fetch in the field component itself.
async function fetchNewTextC(a, b) {
await new Promise(r => setTimeout(r, 500));
return `textA: ${a}, textB: ${b}`;
}
const MyField = props => {
const {
values: { textA, textB },
setFieldValue
} = useFormikContext();
const [field, meta] = useField(props);
React.useEffect(() => {
let isCurrent = true;
// your business logic around when to fetch goes here.
if (textA.trim() !== "" && textB.trim() !== "") {
fetchNewTextC(textA, textB).then(textC => {
if (!!isCurrent) {
// prevent setting old values
setFieldValue(props.name, textC);
}
});
}
return () => {
isCurrent = false;
};
}, [textB, textA, setFieldValue, props.name]);
return (
<>
<input {...props} {...field} />
{!!meta.touched && !!meta.error && <div>{meta.error}</div>}
</>
);
};
I made an example codesandbox so you can play around with this: https://codesandbox.io/s/formik-v2x-async-dependent-field-4yjrv
@jaredpalmer thanks for the quick and detailed response, this is extremely helpful! I have to dig into some of the tools you're using in this like React.useEffect and defining a Field using useFormik and useField (I am just defining Field in the template and using the render callback to access meta/form.values/form.setFieldValue), but this answers my question to a T.
Iam searchig for hours - and quite suprised - that Formik does not provide a nice solution to override onChange / without prop-drilling setFieldValue or use useEffect with useFormik ...
Most helpful comment
This is actually spot on for how you should do it with Redux. If you remove the Redux jargon from your solution above, then all you are really doing is passing some setters and state to pure functions that live outside your form. This is perfect and exactly how you should do it.
In addition, depending on your app, you may not need/want Redux at all here. If your async call
fetchNewTextCis heavily related totextC(which it seems like it is), then you may find it more maintainable to collocate that data fetch in the field component itself.I made an example codesandbox so you can play around with this: https://codesandbox.io/s/formik-v2x-async-dependent-field-4yjrv