Error: Maximum update depth exceeded with ajv

I am trying to save the errors in a variable while validating with ajv. Here is my code:

const [data, setData] = useState(initialData);
const [errors, setErrors] = useState([]);
const handleDefaultsAjv = createAjv({useDefaults: true});
return (
    <div className="App">
        <JsonForms
            schema={schema}
            uischema={uiSchema}
            data={initialData}
            renderers={materialRenderers}
            onChange={({ errors, data }) => {
                console.log(data);
                setData(data);
                setErrors(errors);
            }}
            ajv={handleDefaultsAjv}
        />
    </div>
);

I am getting this error: Error: Maximum update depth exceeded. This can happen when a component repeatedly calls setState inside componentWillUpdate or componentDidUpdate. React limits the number of nested updates to prevent infinite loops.

The error can be fixed by either removing the ajv property or setErrors call. It looks like in the presence of the validator, the setErrors call triggers an infinite recursion which I cannot understand. Any ideas why this might happen?

[original thread by Albert Gevorgyan]

[Albert Gevorgyan]

Another problem with the above code is that the form is not responsive - nothing happens if I type in the fields. The only way to make it respond is by setting the data property to the data state which is then updated in oChange call. But this results in the same error in the presence of ajv.

The cause of the overflow is that with each (re)render a new Ajv instance is created. JSON Forms will re-initialize itself and emit an update event. As the state in your component is then updated a new render will be triggered, also creating a new Ajv instance.

To fix the problem you can either move the Ajv instance out of the component, add it to the state or wrap it in useMemo.

Another problem with the above code is that the form is not responsive - nothing happens if I type in the fields. The only way to make it respond is by setting the data property to the data state which is then updated in oChange call. But this results in the same error in the presence of ajv.

That’s also the case because of the re-initialization of JSON Forms as it then always uses the initialData. Should be automatically fixed with the suggestion above.

[Albert Gevorgyan]

Thanks for the quick reply! Just moving the ajv outside of the component fixed the problem. Is it a good idea in general to use the same data object for initialisation and updating, e.g:
<JsonForms schema={schema}
uischema={uiSchema}
data={data}
renderers={materialRenderers}
cells={materialCells}
onChange={({errors, data}) => {
setData(data);
setErrors(errors);
}}
ajv={handleDefaultsAjv}
/>

At the moment this is not recommended as we rerender and revalidate the incoming data object when it’s changing, so you’re then doing this twice. For small forms it’s not really noticeable but for large forms this is too much overhead. Therefore I would recommend to stick with the initialData approach from above. If you have advanced use cases where you need to change the incoming data object while also setting it manually (for example a clear form button) you can usually make it work using useRef.

Note that for the next release 2.5.0 we’re planning to add a specific check to detect cases like this to make JSON Forms easier to use correctly.

[Albert Gevorgyan]

Thanks!

[Albert Gevorgyan]

I have a problem again with the validator. I reset the schemas and the data upon a user action, but after doing so the data object still contains a remaining field with a default value from the previous schema. I can get rid of it by using the “removeAdditional: true” option in createAjv but it seems like a hack. Is there a better solution to this?

When you use the useDefaults Ajv you’ll immediatly get an event from JSON Forms with the generated default data, could this be the problem here?

If not, can you post the code of how exactly the JSON Forms invocation and the reset looks like? It’s hard to guess what the problem could be without the exact context.

[Albert Gevorgyan]

It is probably caused by the event from Ajv but I don’t know how to prevent it from sending that event after changing the schema and data. Is there a way to “clear” the default? Re-creating the ajv upon resetting the schema doesn’t help.

If I understand the issue correctly then when you reset the whole data object and you don’t want Ajv to fill in the defaults then you need to also use an AJV without useDefaults after reset. Alternatively you could also hand in a schema without any default entries when the user clears the form.