Open LouaiCoolshop opened 1 year ago
I think I have simpler example of the same issue: https://codepen.io/stusu/pen/rNKRmbM The issue here is that I have 2 custom fields which both calles onChange (from useEffect) at the same time, what causes that the first component does not get new value. Please check the console for the example I provided, it clearly shows that first component called onChange with new value but it was not passed to it through props.formData
@Stusu @LouaiCoolshop The problem here is that the formData is stashed in a state variable on Form
and the second onChange()
causes the state to be updated with the original state and not the updated state. Remember react merges state in its own time, not immediately. Our code, currently does the bad thing, and not the correct thing (sorry). Do either of you feel comfortable fixing this in the 5.0.0 release?
@heath-freenome , Before proceeding on solution recommended, I would like to confirm my assertion about the fix.
The fix is on these line in the onChange method
this.setState(
state as FormState<T, S, F>,
() => onChange && onChange({ ...this.state, ...state }, id)
);
and update the logic as below
this.setState(
(oldState) => ({
...oldState,
...state
} as FormState<T, S, F>),
() => onChange && onChange({ ...this.state, ...state }, id)
);
@balajiram @heath-freenome you may also need to modify onPropertyChange
in ObjectField.tsx (link to code).
Ideally, a PR would include a test that uses a simple custom field like in @Stusu 's example to prove that the issue is fixed.
@nickgros Since the only place that the state is set is in Form
I'm not sure onPropertyChange()
is necessary
@heath-freenome Updating the setState with async syntax on "Form" onChange method didn't fix the change.
Reasoning? (with Example 2) useEffects starts asynchronously after the component render. So effects have old state values in their scope(state value before effects start). So the value changed one effect is not available in the scope of the other effect, so form state is not getting updated properly here.
Example 2 with updated setState changes here. check the console.log statement to assert the reasoning given.
In component updating field1 value yyy -> yyy-changed
In component updating field2 value zzz -> zzz-changed
Inside @rjsf/core Form onChange
oldState field1: yyy field2: zzz
newState field1: yyy-changed field2: zzz
Inside @rjsf/core Form onChange
oldState field1: yyy-changed field2: zzz
newState field1: yyy field2: zzz-changed // here field1 value should be "yyy-changed" but its "yyy"
Inside @rjsf/core Form onChange
oldState field1: yyy field2: zzz-changed
newState field1: yyy-changed field2: zzz
Inside @rjsf/core Form onChange
oldState field1: yyy-changed field2: zzz
newState field1: yyy field2: zzz-changed
Example 1 : Updating the state inside the effects is not recommended, check here on more details. So can you update the logic and try?
Ah, as it turns out the issue really is in the onPropertyChange()
function that @nickgros pointed out. There is no simple fix for this. In fact, it would likely take a reimplementation of how we update formData in containers like objects and arrays. This is no simple feat as it would likely result in a major breaking change. Sorry we don't have an easy fix.
Hi, everyone! I have discovered that there is a flushSync
API in React DOM. Perhaps this can help with synchronizing the state updates?
Prerequisites
What theme are you using?
core
Version
4.2.0
Current Behavior
In my react app, I have a form with a few fields. Two of them use a custom field. What I'm trying to do is that when the 1st one's value changes, the 2nd one should clear.
To do that, I'm passing all the form's data into the form context, and making the 2nd field check when the 1st field's value changes (using useEffect), and call "onChange(null)".
The problem is that sometimes the 2nd field's value doesn't nullify. It would call "onChange(null)" but the "formData" for it does not update, and the field will stay the same.
But, if I put that "onChange(null)" into a setTimeout for 50ms, it works always.
The custom field (simplified):
I'm certain that the "onChange(null)" is being called when I need it. Even used the debugger and stepped in. But sometimes it doesn't update the field formData to null when I don't have that setTimeout.
Expected Behavior
When I call "onChange(null)", it should always update the formData instead of just sometimes, without needing to use a setTimeout.
Steps To Reproduce
Environment
Anything else?
No response