Currently Formik is missing a feature to have a callback when Formik changes a field value. Example when setFieldValue('bip', 'something') it would be nice to have a callback that would also do the other work necessary.
Currently I add onChange handlers to my components which uses Formik's setFieldValue along with my own change handler (imagine if when you select an address, you want to immediately load the shipping cost before submitting the order). However doing that creates a significantly more complex codebase, and I can't take unit testing shortcuts like calling the setFieldValue prop and checking if all handlers invoked. Additionally when writing components I can make them just generically handle how Formik works and wire all those weird change handlers using the enhanceWithFormik HoC or the Formik component.
Just like we have an API for the handleSubmit onSubmit I am looking for the following APIs:
onChange(values)onFieldChange({ bip: (value) => {}, bop: (value) => {}})Let me know if this jives well with the Formik philosophy. And what you prefer to do instead. If it jives I'll take a stab at a PR.
Hey thanks for taking the time to fill out the feature suggestion. Formik is an uncontrolled component though. Adding onChange would inevitably encourages ppl to try to sync state in 2 places. Please see the form middleware / state reducer RFC.
This is the third or fourth duplicate of this issue/suggestion, so I think it鈥檚 time to add a section to the docs.
Looking at #401 I have some criticisms in regards to this use case I described. If the use case I provided is indeed useful then I feel the middleware is an overly complex solution but may be of some help.
I hope this use case is something already accounted for. Hoping to reduce the bolierplate / complexity of my code. :)
I understand your frustration and agree that a solution is needed.
onFieldChange would require Formik to calculate a bunch of diffs which I would not subject all users too.onChange(values, bag) is probably the wrong name for that function. Would suggest calling it onChangeValues. Regardless, I still am not convinced that this should be part of the top level component API as these kinds of changes can be implemented with componentDidUpdate with a declarative component that renders null. However, to experiment with this outside of core, I'm going to publish said component today so that people can see this first hand. Stay tuned.Excited! Thank you.
Nice. Is there an HoC version of that? I know I'm super picky :P
I do like this pattern it makes sense in the context of how formik manages state. Thank you for the quick turnaround!!!!!
An HoC can be implemented in user land like so:
const SuperFormik = ({ render, onChange, ...props }) =>
<Formik
{...props}
render={p => <><Effect onChange={onChange} />{render(p)}</>}
/>
const withSuperFormik = (config) => Comp => (props) =>
<SuperFormik {...config} render={fp => <Comp {...props} {...fp} />}/>
I thought this pattern was widely known and used. Will think about ways to incorporate it into docs.
I'm just saying that since Formik ships with an HoC it would probably make sense to also ship with one here.
I have a connected Filters component which receives filters as props from store and also saves them to store after every change because they have to be persisted.
This is how I ended up using Effect to prevent infinite loops:
<Effect onChange={(currentFormikState, nextFormikState) => {
if (!isEmpty(nextFormikState.values) && !isEqual(currentFormikState, nextFormikState)) {
onChanged(nextFormikState.values);
}
}}/>
@jaredpalmer what do you think? Is this how you imagined this would be used?
Or do you think I should dump formik in this specific case because it was meant to do other things?
In any case, I am closing this issue. Formik-effect is a pretty good solution for what is needed. THANK YOU!
The code I ended up using for HoC is:
import { withFormik } from 'formik';
import { Effect } from 'formik-effect';
import { Fragment } from 'react';
export const withFormikAndEffect = ({ onChange, ...rest }) => BaseComponent => {
return withFormik(rest)(props => (
<Fragment>
<Effect onChange={onChange.bind(this, props)} />
<BaseComponent {...props} />
</Fragment>
));
};
"Effect" did not work for me with HOC on latest version, here's a workaround https://github.com/jaredpalmer/formik/issues/766#issuecomment-465413540
Most helpful comment
Hey thanks for taking the time to fill out the feature suggestion. Formik is an uncontrolled component though. Adding onChange would inevitably encourages ppl to try to sync state in 2 places. Please see the form middleware / state reducer RFC.
This is the third or fourth duplicate of this issue/suggestion, so I think it鈥檚 time to add a section to the docs.