Init onChange with multiple forms

We’re using JSONForms with React. Some of our forms are quite large, so we’ve split them into sections for improved performance, and we maintain the form and UI schemas for the separate sections in a database, and then render multiple form sections on the page at the same time using multiple instances of the JSONForms component. Ultimately we want to merge the form data together, so we use a common onChange handler; we invoke it with a “part number” so that we know which part is being modified. Basically, like this:

 onChange={dataAndErrors => ourFormChangeHandler(partNumber, dataAndErrors)}

So the challenge we’re having is: we need to know if ANY of the form “parts” have validation errors, so that we can disable the “submit” button. This all works fine as the user makes changes to individual fields. But on init, when the multiple form parts are first rendered, our form change handler gets called by each of the instances of the JSONForms component, and those calls happen asynchronously. We’ve had to jump through some hoops in order to capture all of the data in our container’s state, as well as the presence of any validation errors. And since our change handler doesn’t know whether it’s being invoked due to a change by the user vs. at init time, we have to take the careful approach in all cases; it seems to be working, but performance is not ideal. If the onChange callbacks during init were synchronous, we could do it in a more straightforward way (or, if there were an additional parameter passed in, telling us that it was happening at init time).

Are there any suggestions for how we might approach this in a different way?

[original thread by Kevin Ilsen]

Hi @kevin-ilsen(kevin-ilsen), we could adapt the code in JSON Forms to add whether the emitted change event is the initial one, however that would complicate the emit code quite a bit. When we do this, we could even do it in a synchronous way. I’m not sure that this is the best way to go. We’re even thinking about eventually performing the validation asynchronously by default.

If you just need an indicator about the initial event you can do this quite easily by wrapping JSON Forms and keeping track of it yourself:

const MyJsonForms = props => {
  const initial = useRef(true);
  return <JsonForms
                 {...props}
                  onChange={ event => {
                     props.onChange({...event, initial: initial.current});
                     initial.current = false;
                  }}/>
}

There also two other approaches in principal:

  • You could just validate the data yourself and initially enable/disable the submit button based on the validation result. If you don’t yet customize Ajv you can simply create an instance via createAjv exported from @jsonforms/core. Downside is that all the data will be initially validated twice. One by you and then again by JSON Forms.

  • Last but not least you could just reverse the behavior: Disable the submit button until everything validated successfully. This is probably the most generic and easiest approach and also works asynchronously without additional effort.