# Render nested forms in flat structure

**URL:** <https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591>\
**Category:** General\
**Created:** [July 11, 2023, 12:18pm UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591 "2023-07-11T12:18:28Z")\
**Posts on this page:** 5\
**Page:** 1

<div class="post-metadata">

**Author:** ![bangank36](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/bangank36/32/764_2.png) [@bangank36](https://jsonforms.discourse.group/u/bangank36)\
**Post date:** [July 11, 2023, 12:18pm UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591/1 "2023-07-11T12:18:28Z")

</div>

Hi everyone,  
This is follow up discussion of [add a deep ui schema generator by weiner · Pull Request #1666 · eclipsesource/jsonforms · GitHub](https://github.com/eclipsesource/jsonforms/pull/1666#issuecomment-1626778036)

- Summary  
In the process of developing custom renderers for Gutenberg Components, I want to integrate Navigator Component because I want to display nested props on separate views and only use single schema

To render the Navigator Component markup] requires the form screens in on a flat structure, therefore current jsonforms renders the nested props in nested markup.

The original question was about the need of a uischema generator to get all the form screens from root node, but then I decided to have a new context to store the screens content ( which is `JsonFormDispatch` ) [WP-Builder: Feat/nested Navigator context by bangank36 · Pull Request #27 · bangank36/WP-Builder · GitHub](https://github.com/bangank36/WP-Builder/pull/27)

- ObjectRenderer update its screen via context and only display the NavigatorButton
- The new NavigatorLayout renderer receives the screen content from nested props and render its list of NavigatorScreen

![chrome-capture-2023-6-11](https://global.discourse-cdn.com/free1/uploads/jsonforms/original/1X/947dca99ac66bf9d89817d2babd2a8c0ef8ac850.gif)

Though this approach works well as you can see in the attached recording above, as it is compatible with jsonforms current features set such as show/hide rules, you may see the obvious issue here with Context usage: Provider and Consumer unnecessary re-rendering

```auto
useEffect(() => {
    // Use the callback since the new state is based on the previous state
    setScreenContent(prevScreenContent => ({
      ...prevScreenContent,
      [route]: {
        component: (<JsonFormsDispatch
          visible={visible}
          enabled={enabled}
          schema={schema}
          uischema={detailUiSchema}
          path={path}
          renderers={renderers}
          cells={cells}
        />),
        label: detailUiSchema.label,
        path: path
      }
    }))
  }, [route])

```

I hope you can help me to improve the context usage to optimize the app and if there is built-in feature that can help achieve the similar requirements (render nested object in flat structure), I am happy to know

cc: @sdirix  
Thank you!

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [July 12, 2023, 2:20pm UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591/2 "2023-07-12T14:20:54Z")

</div>

Hi @bangank36,

> The original question was about the need of a uischema generator to get all the form screens from root node,  
> […]  
> if there is built-in feature that can help achieve the similar requirements (render nested object in flat structure)

The nested rendering happens because:

- Every nested object is rendered by the object renderer
- The object renderer generates a UI Schema for the object and dispatches again
- The generated UI Schema is a `Group` which is why it looks nested

An easy way to render flat lists therefore is to overwrite the generic `Group` renderer with an own implementation and simply let it generate a `VerticalLayout` instead of a `Group`. In the DOM the controls will still be nested in layout divs, however by default the VerticalLayout has no margin nor padding so it is not visible.

As an alternative you could of course adapt the UI Schema generation more deeply and instead of rendering nested objects via an object control, look further into them and generate controls for the nested properties directly.

> I hope you can help me to improve the context usage to optimize the app

The used approach can’t be optimized as it rerenders by design, i.e. the parent component is informed about new elements during rendering of one of its children. This will always rerender no matter what you do. With the usage of `React.memo` parts of it could be improved, but the overall issue will remain.

The clean solution is to get rid off the context and determine beforehand which screens you want to show. As the screens are aligned with the use of objects this can be analyzed beforehand in a straightforward manner. Did you try the approach which I suggested [here](https://github.com/eclipsesource/jsonforms/pull/1666#issuecomment-1628409427)?

---

<div class="post-metadata">

**Author:** ![bangank36](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/bangank36/32/764_2.png) [@bangank36](https://jsonforms.discourse.group/u/bangank36)\
**Post date:** [July 14, 2023, 3:38am UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591/3 "2023-07-14T03:38:05Z")

</div>

> [@](#):
>
> Did you try the approach which I suggested [here](https://github.com/eclipsesource/jsonforms/pull/1666#issuecomment-1628409427)

Yes, thank you for suggested, please find the detail report here with recording [POC: Update navigator layout by bangank36 · Pull Request #36 · bangank36/WP-Builder · GitHub](https://github.com/bangank36/WP-Builder/pull/36)  
This is good approach, but it will only works with 01 level of nested data, if we have more level, then the 2nd level will display on the same screen as shown in the screenshot

> [@](#):
>
> In the DOM the controls will still be nested in layout divs, however by default the VerticalLayout has no margin nor padding so it is not visible

Actually I am concern about the nested DOM nodes, while the NavigatorProvider of Gutenberg requires all the screens to be on the same level of the main screen, that why I am looking for effective way so that all the deep nested screens can be rendered on the root node, which requires a deep generated UISchema or context. ( again, am I missing a built-in feature that can do that? )  
I am aware context makes providers and consumers re-rendered by design, but I will try to improve performance using useMemo somehow  
Attached is the recording of what have been implemented  
 ![chrome-capture-2023-6-11](https://global.discourse-cdn.com/free1/uploads/jsonforms/original/1X/947dca99ac66bf9d89817d2babd2a8c0ef8ac850.gif)

Thanks for helping

---

<div class="post-metadata">

**Author:** ![sdirix](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/sdirix/32/1073_2.png) [@sdirix](https://jsonforms.discourse.group/u/sdirix)\
**Post date:** [July 17, 2023, 12:17pm UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591/4 "2023-07-17T12:17:08Z")

</div>

Hi @bangank36,

You still don’t need deeply generated UI schemas for the deeply nested cases. You just need to hand over the parameters to the JSON Dispatch accordingly.

Should look something like this. Note that I wrote this down without testing. Code can obviously be optimized to not join the path array all the time, etc.

```auto
/**
* collects all (nested) screens in the contained schema and returns appropriate rendering information
* expects and returns path in array format
*/
const analyzeSchemaForScreens = (schema, path) => {
  const result = [];
  Object.entries(schema.properties ?? {}).forEach( ([key, value]) => {
    if(value.type === 'object') {
      result.push({ schema, uischema: control(`#/properties/${encode(key)}`), path };
      result.push(...analyzeSchemaForScreens(value, [...path, key]));
    }
  });
  return result;
}

const NavigatorLayoutRenderer = (props) => {
  const screenInformation = useMemo(() => analyzeSchemaForScreens(props.schema, props.path.split('.'), [props.schema, props.path]);
  return (
  <NavigatorProvider initialPath="/">
      <NavigatorScreen path="/">
        <p>{{props.label}}</p>
        {{screenInformation.map(screenEntry => (
          <NavigatorButton path={`/${screenEntry.path.join('/')}`} key={screenEntry.path.join()}>
             Navigate to {{screenEntry.path.join('/')}}.
          </NavigatorButton>
        )}}
      </NavigatorScreen>
      {{screenInformation.map(screenEntry => (
        <NavigatorScreen path={`/${screenEntry.path.join('/')}`} key={screenEntry.path.join()}>
           <JsonFormsDispatch schema={screenEntry.schema} uischema={screenEntry.uischema} path={screenEntry.path.join('.')}/>
        </NavigatorScreen>
      )}}
  </NavigatorProvider>);
}

const control = (scope) => ({
  type: 'Control',
  scope
});

```

Edit: Note that for the objects contained within you want to have a custom object renderer which replaces nested object rendering with `NavigatorButton`s. You can then either determine the `NavigatorButton`’s path by converting your props, or you make the `screenInformation` available via a context from the `NavigatorLayoutRenderer`. This is fine here as the context is created once and not modified on the fly during rendering.

---

<div class="post-metadata">

**Author:** ![bangank36](https://yyz2.discourse-cdn.com/free1/user_avatar/jsonforms.discourse.group/bangank36/32/764_2.png) [@bangank36](https://jsonforms.discourse.group/u/bangank36)\
**Post date:** [July 17, 2023, 2:58pm UTC](https://jsonforms.discourse.group/t/render-nested-forms-in-flat-structure/1591/5 "2023-07-17T14:58:57Z")

</div>

> [@sdirix](#):
>
> You still don’t need deeply generated UI schemas for the deeply nested cases.

> [@](#):
>
> …hand over the parameters to the JSON Dispatch accordingly

Yeah I agreed, this is also my approach to not lean on the deep nested uischema, as described I let the nested props generate its own uischema and [pass to root](https://github.com/bangank36/WP-Builder/blob/97b6ec492ac8680b02c2ff1c0858d384ee8ce33f/src/js/renderers/ObjectRenderer.js#L67) via context

> [@](#):
>
> have a custom object renderer which replaces nested object rendering with `NavigatorButtons`

Agreed ([ref](https://github.com/bangank36/WP-Builder/blob/97b6ec492ac8680b02c2ff1c0858d384ee8ce33f/src/js/renderers/ObjectRenderer.js#L88))

I am moving on with the Array renderer since it need to iterate over the data to render the items
