I use JSONForms 3.4.1 with React Material Renderers. The form is a bit complicated because I use a combination of all layouts and cannot share all the details here. The main issue is that there are 4 categories in the categorization. Any edit I make in the Category 3 form would select the Category 1 tab if validation errors exist. If there are 10 text fields, entering each letter would create this behaviour. What are the possible reasons for this behaviour?
Hi @ganapatib,
The category selection state is stored within the component, not in the form-wide state.
- Initially the state is set to the first category
- Clicking on a tab will store the new tab as the active one
- We also reset the active category in case the UI Schema changes
So it seems that in your case, whenever you edit the Category 3 form, a new UI Schema is handed over to JSON Forms. The renderer realizes that the UI Schema changed and therefore resets.
I would like to recommend checking why a new UI Schema (instance) is handed over in your case. In general it makes sense to hand over stable props to JSON Forms to minimize rerenderings and re-evaluations.
Thanks for the response @sdirix. I am not changing the UI schema. Here is the usage.
const schema = ...;
const uischema = ...;
const [formData, setFormData ]= useState(...init_data...);
// setFormData is invoked in the handleChange method which is triggered on `onChange`
return (
<JsonForms
schema={schema}
uischema={uischema}
data={formData}
...
/>
);
One thing is that one of the Category elements is displayed based on rule conditions. For example, each Category will have a dropdown (Types of data source - API, database, SFTP), and for each drop-down value, the next element changes (API configuration, database configuration, SFTP configuration, etc). Would this cause this issue?
Hi @ganapatib,
No, rules do not have an affect on this. They are managed separately, see here.
It would be great if you could construct a schema and UI Schema with which I can reproduce the issue with our React Material renderers. In case you have custom renderers in play, there can be all kinds of different causes. For example you might not be memoizing findUISchema calls and thereby generate a new (sub) UI Schema with each render pass.
I thought about it some more. There is a use case which is currently not supported: If you use rules so that data changes in a category affect the visibility of a previous category, then all kinds of shenanigans might occur.
This is the case because we use the index of the category to save which category is the active one of the current visible categories. If a later category affects earlier ones, the index, although unchanged, will suddenly point to a previous category or it will be out of bounds, in which case it will be pointing to the first category again.
So if that is the issue which you are facing I would recommend to check whether you can adjust the order of your categories.
If you like you can also contribute an enhancement. There is no technical need for the index to refer to only visible categories. Instead we should use the index of all potential categories and adapt the “switch” functionality to make sure that invisible categories are skipped.
Doing that we could also support the use case described above.
@sdirix I debugged this a little more and found that the activeCategory is set to 0 when categorization is not same as previousCategorization which is the block here. And checking the contents of both objects, they are same which means they are just different instances. But the uischema instance passed is always the same. Any guidance here?
And to your question of whether data changes in one category affect the visibility of another category, no. All categories are always visible. Only the visibility of category elements is rule-based.
Adding more details after more debugging:
I observed that the component re-renders after an edit in any input in any category. Tracing why the re-rendering occurs, I found that two state changes cause it: one is the formData updated on invocation of onChange, and the other is formErrors also updated on invocation of onChange, and used to enable or disable the submit button. As specified earlier, this is how my usage looks like:
const schema = ...;
const uischema = ...;
const [formData, setFormData ]= useState(...init_data...);
// setFormData is invoked in the `handleChange` method which is triggered on `onChange`
return (
<JsonForms
schema={schema}
uischema={uischema}
data={formData}
...
/>
);
Do you use any custom renderers which generate or create a UI Schema during rendering? If yes, then you need to make sure it’s memoized so that the instance does not change.
Alternatively you could check whether the category is fully recreated on change. This could for example happen if some React keys are changed “onChange”.
If not, can you produce an example schema and uischema with which I can reproduce the issue? At the moment I can only do guess work.
I have a custom renderer but doesn’t change the UI schema. Here is the reproducer with using only the material renderers available.
Hi @ganapatib,
In your example the props are not stable at all.
This is how it looks like:
Component App
|- const schema = ...
|- const uischema = ...
|- const renderers = ...
return <JsonForms schema uischema renderers />
As schema, uischema etc. are created within your renderer code they are newly constructed with every single render pass. So you hand in a full new schema and uischema with every change.
Not only will JSON Forms rerender every single element every time (as you hand in new instances), it’s also why the category is resetted.
To solve this you need to move the schema, uischema, renderers etc. outside of the component or place them in useMemo or useState.
Unrelated to the issues above: You are updating the data with every handleChange invocation. This is better done within a middleware to avoid an unnecessary render pass. See here for the documentation.