I’ve noticed that inside the JsonForms library, the JsonFormsContext.Provider alongside Redux, there seems to be a potential rendering issue where changes to the Redux state cause all components inside the context to re-render, even if only a small part of the state has changed. This could impact performance, especially in larger forms.
Could you please explain the design decision behind using the Context API in this way, especially when Redux already provides state management?
I’d like to understand more about:
Rendering Behavior: Was the use of JsonFormsContext.Provider intended to address certain performance concerns, or is it a result of how the context API propagates changes? How do you handle potential unnecessary re-renders when only a small part of the Redux state is updated?
State Distribution: What led to the decision to use both Redux and Context together, instead of relying on Redux alone to manage and distribute state across components? Is the Context API used here to simplify how components access certain parts of the state?
Use Cases and Benefits: What specific benefits does the combination of Redux and Context provide in this setup, and in what use cases does this approach prove advantageous over a more direct Redux-only solution?
A few years ago, before JSON Forms 2.5 (which was released January 2021), we tightly integrated with Redux, i.e. Redux was a mandatory dependency. Since then we first deprecated and then removed the Redux support.
Instead of Redux we are using React contexts to manage our form wide states. It’s either or, we never used both. See also here.
It’s true, that any change to the form wide state, e.g. updating parts of the data, will trigger a rerender of all JSON Forms renderers. However all these renderers are memoed and wrapped in HOCs. In all renderers which are not affected by the change, the HOC will determine the new props for the respective renderer, these props will not have changed and the memo will take effect.
Note that, of course, JSON Forms can still be used with Redux, like any other React component.
While memoization helps prevent unnecessary DOM updates, I noticed a laggy experience due to the large JSON Form on the UI, which included complex fields, editors, validations, and other integrated UI components. This led me to start looking into the core implementation to better understand the behavior.
From our experience with Redux in other projects(not in jsonforms), it helps to subscribe only to specific parts of the state. However, with the React Context API, everything under the provider subscribes to changes, causing evaluations across all consumers.
When profiling, I noticed that even a single checkbox click triggered re-evaluation for everything inside the JSON Form. While I understand that memoization prevents unnecessary DOM updates, the profiler still reflects the evaluations happening, even if the DOM isn’t repainted.
Thanks again for your explanation! I just wanted to confirm my understanding.
I agree, all “bindings” are reevaluated with every change which is a conceptual weak point of the JSON Forms’ architecture. Nevertheless these re-evaluations are usually pretty cheap and fast enough to not be noticeable.
Note that this weak point was already in place with our Redux integration: We’re using AJV for JSON Schema validation. Validation will always run form-wide and therefore lead to the re-execution of the bindings on every change.
We discussed the current state of the bindings internally in the past. In the long run we would like to see the bindings refactored to:
be more modular
composable
differentiate between one-time and run-time evaluations
Even when keeping the current context based approach, we could allow to memo large parts of the bindings (e.g. one-time determinations like schema or translations) so that only the data and errors determination is running on change.