-
-
Notifications
You must be signed in to change notification settings - Fork 2.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
onChange for entire form #271
Comments
You will always receive latest |
@prichodko is exactly correct. |
Sorry to reopen a closed thread, but this seemed like the most appropriate place to follow up on this. If I understand correctly, the suggestion is to use another component to simulate onChange responses. I can see how that might make sense for something extremely agnostic like FormikPersist, but for the general situation where you wish to cause some effect based on form changes, this doesn't make sense to me. Consider a simple form which contains dropdowns for the ingredients of a burger. You wish to render a preview of the burger outside the form somewhere. It would seem fairly simple (and conventional) to do this by passing the form an "onBurgerIngredientsChanged" handler, and hooking that up into the form's onChange handler. Am I correct in understand you would suggest instead creating a It seems to me this would very naturally lead people to creating their own Just my two cents - I'm facing this problem now with a few forms that are slightly more dynamic than their siblings. Any thoughts would be much appreciated. Thanks Edit: Furthermore - and I think this is the bigger issue - by binding "onChange" type handlers into the render lifecycle, you're coupling two very different concepts. My form might render itself every second if it's being passed a readonly "counter" value - this doesn't mean any of the values have changed! |
In case someone stumbles upon this thread, I've written a lib for this use case: https://github.com/ramitos/formik-observer |
Hey, These side-effects could be:
Also notice how these usages are actually the only reason why one would use Redux-Form over Formik. Once you have that onChange callback, you can really easily save the state in Redux if you want to, which nullify the need of using Redux-Form and allow all the usages I said above. |
See #401 |
Not something I'm proud of, but I hooked into the // Rough sketch
class Component {
validate = values => {
// Do what you want with the values
console.log(values)
return {}
}
render() {
<Formik validate={this.validate} />
}
} |
@ramitos - Great little package, perfect for what I needed it for. |
Founded some interesting solution of this issue. |
@vsamotskiy What if I wanna update my component state in |
I can't believe why I can't use properly and simply the onChange property. |
Since Componentimport React, { useEffect } from 'react'
interface Props<T> {
onChange: (value: T) => void
value: T
}
export function FormikObserver<T>(props: Props<T>) {
useEffect(() => {
props.onChange(props.value)
}, [Object.values(props.value).join(', ')])
return null
}
FormikObserver.defaultProps = {
onChange: () => null,
} Usage<Form>
{(formProps: FormikProps<{}>) => {
return (
<Fragment>
// Your form
<FormikObserver value={formProps.values} onChange={value => console.log(value)} />
</Fragment>
)
}}
</Form> |
What about doing something like this? Which I think is what @prichodko was saying in <Formik initialValues={initialValues} onSubmit={onSubmit}>
{({ values }) => (
<Form onChange={() => onFormChange(values)}>
{/* Input fields */}
</Form>
)}
</Formik> I just ignore the synthetic event that's piped into the Didn't need any Observable third party implementations. |
@ebrearley: it doesn't work for React Native as there is no My use case: I need to verify I think a better solution is more than needed. At least for React Native. |
@dragosdehelean the proper way to handle something like this is to use an effect. An example of this implementation can be seen in:
I'm hoping to add some examples to the documentation in #1787. Let me know if this helps! |
I don't agree that a separate component with an effect is the reasonable architecture. This is essentially diverting from the original (and reasonably sane) form configuration via props to configuration via XML. You don't respond to the form <Form>
{(props) => (
<>
...
<SubmitObserver value={props.values} onSubmit={this.onSubmit} />
...
</>
)}
</Form> Why would Logically both |
@Etheryte I absolutely agree with your argument that it's not intuitive. However, I do think a simple onChange wouldn't provide enough flexibility for all user scenarios, and if we don't find a way to provide for all user scenarios, there isn't a strong case to expand the API. Feel free to open a PR if you have a solution that can both optimize for renders when states are identical and allow users to circumvent this optimization if needed in a way that is clear to users. I haven't looked much into the v2 api so maybe there's a simple way now that we have hook support. Edit: the "Form Submit Event" example doesn't really exist in React Native so it doesn't apply to the full audience of Formik. |
Couldn't you just have two separate callbacks? |
As this thread is still quite high up in the google search I post the unnecessary complex but new way here: |
@valoricDe thanks, works perfectly |
Guys, i found a better solution
|
@thushara5884 Using |
@Etheryte yes you are correct. i was hoping formik internally uses children as another component, but it seems it use it as a callback function.
|
This is the best solution i found so far. |
I was ignoring this. I didn't knew how to get global events from |
Did anyone else find that this technique was always one change behind? In other words, the form's onchange is being called before formik has collected the change into the values object? In my case I need this for an autosave - luckily this was being debounced anyway so the delay wasn't an issue but I could see how it would be.... I don't understand their hesitancy in adding a context level onChange event. With Formik's approach being to collect and manage the form state at the highest level it seems fitting that it could report when the form is changing. I have reviewed the approaches taken which boil down to:
Of course the issue here could be that formik internally is unable to easily tell when it's dispatch of new state to React has actually taken effect - so I'd expect it to have to monitor the values object itself - it just feels like it would be better for the library to do this... |
@acuthbert it shouldn't be one render behind, by nature of effects being 1-to-1 with renders. it could be triggering your effect multiple times, like when validation is complete, etc, and so triggering
Or your debounce fn could be using a stale callback. Formik doesn't have validation cancellation when the changes are stale, but unless validation is async it will always run in order. If you use async validation it's possible it would do something like #3231 sets up the foundation for an onChange prop and validation cancellation, as it enables optionally subscribing to state. Generally the Here's the de facto ChangeHandlingForm for v2. Notice it is impossible for values to become stale because the onChangeFn doesn't use values from outside the scope. If you pass values from outside the scope, it could become stale, so make sure those deps are passed to useEffect. interface ChangeHandlingFormProps<Values> {
onChange: (values: Values) => void;
}
const ChangeHandlingForm = <Values,>(props: PropsWithChildren<ChangeHandlingFormProps<Values>>) => {
// in v3 this would be `const { values } = useFormikState<Values>();`
const { values } = useFormikContext<Values>();
useEffect(() => {
// you might modify this to fit your use case
// like adding a `const prevValues = usePrevious(values)` above and checking for equality
// or debounce this whole function but make sure not to use stale values
props.onChange?.(values);
}, [props.onChange, props.onChange && values]);
// Formik's Form component
return <Form>
{props.children}
</Form>
}
const MyForm = () => (
<Formik {...formikConfig}>
<ChangeHandlingForm onChange={values => console.log(values)} />
</Formik>
); |
So using the Any ideas out there for me to implement a form-level onChange within useFormik()? Edit: I ended up using a similar approach to the FormikObserver library, but hacking it together by hand:
|
It seems that callin a function inside the render function with the values inside is always one step behind as mentioned. Using the onChange method on So far the most solid way to accomplish that is creating a component that listens to changes according to the argument values of the render function and add a callback as prop, such as: const FormikListener = ({ values, callback }) => {
useEffect(() => {
callback(values);
}, [callback, values]);
return null;
}; and inside the render function: ...
<FormikListener values={values} callback={handleOnChange} />
... |
@MelMacaluso ended up doing the same... Values weren't updating on the internal |
I think is very flaky and horrible but I had to get going, I wish I could hear take of the lib's creator so we do what is intended instead. @jaredpalmer 🙏🏻 |
Ideally, onChange handler was expected to be already implemented - which wasn't case in 2023. As a quick workaround, I brought in external state inside the form (which is very specific to use case I have). This is the least ugly, highly familiar and logical available solution that worked perfectly just in time. |
The const FormikListener = ({ values, callback }) => {
useEffect(() => {
callback(values);
}, [callback, values]); // <- dependencies always change because callback is always different
return null;
}; That can cause cause an infinite loop if, for example, the callback is doing some async logic and changing the state to show/hide a loader. const handleOnChange = useCallback((values) => {
console.log(values)
}, []);
<FormikListener callback={handleOnChange}> |
Ok, back again 😅 so export function FormikObserver<V>({ onChange } : FormikObserverProps<V>) : null {
const { values, initialValues } = useFormikContext<V>();
const previousValues = useRef(initialValues);
useEffect(() => {
if (!_.isEqual(previousValues.current, values)) {
previousValues.current = values;
onChange(values);
}
}, [values, initialValues, onChange]);
return null;
} |
I have a form where some field's values depend on other fields values. I have been using redux-form and have been able to calculate these values using an
onChange
handler on the top-level form object.I don't see an equivalent using formik, is this right? I thought I could hijack the validation function to do this, but it's not passed the formik bag, only the values to validate.
Another usecase for a form-level
onChange
is automatic persistence without needing to hit a submit button e.g. something like:Another option could be a "submitOnChange` flag.
The text was updated successfully, but these errors were encountered: