Best Approach for Conditional Field Rendering and API Validation with JSON Forms in React

Hello,

I’m working on a JSON Forms project in React and am facing an issue with conditional rendering and API-based validation.

Use Case

In my form, I have fields for visitDate and visitTime. When both are filled, I need to call an API to validate the combined date and time. If the API returns an error, an override checkbox should be displayed, allowing the user to bypass the validation and submit the form.

My Approach

To handle this, I added a hidden boolean field isError. Here’s how I intended to use it:

  1. When visitDate and visitTime are populated, I call the API to validate them.
  2. If the API indicates an error, I set isError to true, and a rule in the schema then shows the override checkbox based on this condition.

Methods Tried

  1. Using onHandleChange: I checked if both visitDate and visitTime are set, then called the API and updated isError based on the response. However, onHandleChange keeps triggering repeatedly, causing an unwanted loop.
  2. Using middleware: I attempted to set isError in the middleware UPDATE_DATA action. Unfortunately, I can’t make an asynchronous API call in middleware, so this approach doesn’t work for my case.

Update

I’ve since implemented a solution using useEffect in React. Now, when either visitDate or visitTime changes, useEffect triggers the API call, and I update the data accordingly. This works as expected!

Question

Is there a cleaner or more efficient way to achieve this in JSON Forms? I’m wondering if there’s an approach that may better fit JSON Forms’ architecture or simplify the code.

Any advice or alternative approaches would be greatly appreciated!

Hi @gr8pathik,

There are many potential implementations, this depends on your exact requirements:

  • whether isError is actually a part of the data object and/or JSON Schema or just a “state”.
  • whether the override and is part of the actual data object or just a checkbox to enable/disable a submit button. Depending on that it could be rendered just next to the form, as an own custom renderer or within a combined renderer for visitDate and visitTime
  • whether the custom validation is “extra” or part of the regular validation, in the latter case you could take a look at the async usage of AJV. That is especially useful if you need to perform the same validation somewhere else, i.e. the backend.
  • whether this is hard coded logic for this one case or needs to be declarable by JSON Schema and UI Schema alone.

From what you wrote I assume that you modeled isError as part of the JSON Schema and/or at least store it within your data object.

To update the isError you can use all of the mentioned options, i.e. onHandleChange, middleware or useEffect. As the validation is async, in all these cases you would always just trigger the validation and then call the setData, i.e. you can just call validateAndSet in onHandleChange, middleware or useEffect without awaiting the result.

const [data, setData] = useState(initialData);
const validateAndSet = async (data) => {
  const isError = await myValidateAPI(data);
  setData(prevData => {
    if(prevData.isError === isError){
       return prevData; // this will not trigger a rerender
    }
    return {...prevData, isError} //  use for example 'lodash/fp/set' for a convenience setter in case the `isError` is more deeply nested
  });
}

Some things to consider:

  • In case you manage a submit button, it must be made aware of this “pending” state as otherwise users might be able to submit invalid data
  • You also might want to visualize this pending validation state somewhere in the UI

Hi @sdirix ,

Thank you for your reply!

I don’t need isError to be part of the JSON Schema. However, I do need isOverride to be included in both the JSON Schema and the data object since I need its value when the form is submitted.

To display isOverride only when there’s an error from the API, I added isError to the JSON Schema and used a rule to conditionally show isOverride when isError is true.

I believe the useEffect approach I’m using to handle the API validation and update the isError state is one of the correct ways to manage this behavior. Would you agree, or is there an alternative approach you’d recommend?

Hi @gr8pathik,

Adding a rule on the isOverride and placing isError into the data object is a good workaround to avoid providing a custom renderer. If a custom renderer is also on the table for you, then I would rather not “pollute” the data with the isError attribute. Instead this additional validation is consumed within the custom renderer (e.g. via a React context, additionalErrors or via the form-wide config) and then the field is shown/hidden accordingly.

Nevertheless this is is likely not that important overall.

When keeping the current approach you could clean it up a bit by:

  • Not adding the isError to the JSON Schema. The rule will work fine without it
  • Removing the isError from the data object before sending it further to your services/backend etc.

The useEffect approach will work fine. However technically there is no reason to “wait” for useEffect and build up this chain of interactions. You can just simply trigger the validation directly within your onChange, middleware or custom renderer as outlined in my last post.