I have been experimenting with experimental support for hyrbid react in vaadin 24, and even though it is experimental it seems to be working faily well.
As part of this experiment I am trying to convert the business app starter application, which currently uses Java-based layouts, into a hybrid application with a React front-end. The reason for this is to evaluate the effort required to transition one of my current flow applications running in production to a React hybrid application in the future, when it becomes generally available.
One requirement is to convert the layouts defined in the Flow app to use the App Flow layout. So far, this has worked reasonably well, but I am unsure how to attach the Application Bar to the navigation bar within the App Flow layout.
Here is my crude attempt to attach the Application Bar to the App Layout Navbar:
{/* Content of app-bar goes here */}
In the java components:
addAttachListener(event → {
String jsCode = String.format(“”"
const appBar = $0;
const appLayout = document.body.querySelector(‘vaadin-app-layout’);
const placeholderDiv = appLayout.querySelector(‘%s’);
// If placeholderDiv has any children, remove them
while (placeholderDiv.firstChild) {
placeholderDiv.removeChild(placeholderDiv.firstChild);
}
placeholderDiv.appendChild(appBar);
“”", APPBAR_PLACEHOLDER_ID);
UI.getCurrent().getPage().executeJs(jsCode, appHeaderInner.getElement());
I am wondering if there is better way to do that? thanks for any ideas.
Assuming that the app’s layout is defined in TypeScript, which is straightforward to work with in pure React/Hilla, how should we handle mutating parts of the layout, such as the navbar, in Java views if the same layout component is shared in both java/hilla-react?
When working with pure Lit, it was possible via LitTemplate to implement something like this:
I assume the question is how to keep some parts related to the main layout in Java even though the core of the layout is implemented in Hilla.
The “proper” way of doing that is still on our roadmap in the form of making it easy to add use Flow components from Hilla. You can do that manually already by exporting it as a web component and jumping through some hoops to use that web component from React. We did also find some timing issues that might cause problems when we internally prototyped this approach.
The described approach using executeJs to inject a DOM element managed by Flow into a specific location in the DOM structure creaed through Hilla is also generally a good approach here even though I agree that it looks a bit ugly. You don’t show how the appHeaderInner component is attached but I would suggest using the low-level Element::appendVirtualChild API to attach it to ui.getElement() since that signals to Flow that it shouldn’t even try to attach the DOM element anywhere on its own.
CustomLayout won’t (directly) help in this case since the initial DOM structure is owned by React. There isn’t even any Java instance that directly corresponds to that React component instance. Might still be some benefits from the same approach with injecting a Flow element (tree) into some arbitrary element that is managed by JavaScript. I would spontaneously reuse terminology from e.g. React for this and call it a “portal”.
“I assume the question is how to keep some parts related to the main layout in Java even though the core of the layout is implemented in Hilla.”
!00% Correct; the core of the layout is in React, but I want to mutate/control some parts of it through Java.
“You don’t show how the appHeaderInner component is attached”
I initially attach this to the DOM, and then later, I append it to the app flow’s navigation bar. I was expecting this not to work , but to my surprise, the browser removes it from the original parent and reattaches it to the element I want.
if (appHeaderInner == null) {
appHeaderInner = new Div();
appHeaderInner.addClassName(“app-header-inner”);
getElement().insertChild(0, appHeaderInner.getElement());
}
…
addAttachListener
“using the low-level Element::appendVirtualChild API to attach it to ui.getElement()”
This is an excellent suggestion; I will explore it. I assume there is Java Element API to do that?
@original-uguisu Portal concept seems quite appropriate for use cases like it.
Unrelated to this topic: @quirky-zebra had sent me link to the ideas of using signals, it was excellent read, some new concepts unique to the full stack hillvaadin. Additionally, I noticed that you have already created some proof of concept work with the Preact library. I’m looking forward to seeing this gain traction, as state management in React can be complex, especially for full-stack developers who jump between backend and frontend.
After giving it some thought, I think the Portal API should be more flexible on these two dimensions:
More flexible ways of defining exactly how someComponent should be attached relative to the reference location. I could imagine at least these different strategies
Append as a child
Replace any existing children
Insert as a sibling before or after. These could be particularly useful combine with the Lit trick of using comment nodes as the reference.
More flexible ways of finding the target element rather than only using CSS selectors. The ultimate format is probably a (owner: Element) => Promise<Node> callback (passed as an excuteJs string) and then we can provide shorthands for doing things like CSS selectors on top of that.
With those additions, the API could use a fluent approach.
I am not sure if this helps or confuses even more as there are so many avenues how to do things. It is possible to make React render a view as web component. You can then use that web component in Flow.
`import TodoView from ‘./views/todo/TodoView’;
import { createRoot } from ‘react-dom/client’;
import { utility } from ‘@vaadin/vaadin-lumo-styles/utility.js’;
import { badge } from ‘@vaadin/vaadin-lumo-styles/badge.js’;
class AppExport extends HTMLElement {
mountPoint!: HTMLSpanElement;
I am just wondering, should you consider following other options 1. Have AppLayout fully in Java 2. Build custom app layout either with Java or React that fits better to your requirements.