How to integrate React into a Java legacy GWT client

Julien Sanmartin
7 min readFeb 28, 2019

--

Let me tell you a little story. You and your team are working on a product that is functioning well but the technologies used are from another decade and it is becoming harder to implement new fancy features. So your team decides (sometime after many years) that it is time to upgrade the client with brand new technologies. At first the idea seems pretty cool, you will be able to use the latest trendy frameworks and libraries that everyone is talking about. But soon enough you wonder how you will integrate the new stuff with the old fart code. Well unfortunately it won’t be easy especially if the code you are working on is very old. But us developers have a few more tricks up our sleeves.

Today popular libraries and frameworks for front end development are JavaScript centric : React, Angular, Vue. The architecture that these technologies bring is completely different from what we were used to and integrated them smoothly into legacy code can be tedious to say the least.

We will see in this article how to integrate React components into a GWT container and make them communicate with each other.

To understand the challenge it’s important to know that GWT (Google Web Toolkit) was created 12 years ago and popular a decade ago so the code developed with this framework can be very old fashioned especially compared to the Component based paradigm that React, Angular or Vue are all about.

To demonstrate we will build a very simple GWT Widget that will display a Circle. The Circle will be rendered using React with a specific colour and diameter coming from GWT. When the user will click on the Circle we will send back the current date to the GWT Widget.

We will work with 3 files : a Java Class that will be our GWT Widget, a React component called Circle and a JavaScript middleware that will be the glue piece between the GWT Widget and the React component.

First let’s create a React component that will just render a Circle. We can choose the color and diameter though the props and when we click on it we will send back the current date to the parent through the onClick callback. We also export the Circle component in order to import it later in the middleware.

Then let’s create the JavaScript middleware code that will create/unmount our Circle component and handle callback clicks. This middleware will expose two functions (renderCircle and unmountReactComponent) through a global object ShapeViewer.

First thing to notice is that the code is wrapped around an IIFE (Immediately Invoked Function Expression) which will allow the code to be executed as soon as the SimpleGWTReactMiddleware.js file is invoked. We pass to the IIFE the global variable window to which we will add our ShapeViewer object containing two functions (renderCircle and unmountReactComponent). That will allow any JS code running inside the same window browser to have access to the ShapeViewer object through window. It’s important because this is how the GWT Widget will be able to access to the React component, we will see that in a little while.

The code in the middleware is pretty straight forward. We import React, ReactDOM and also our component Circle. Then the renderCircle function through ReactDOM.render injects the Circle component to the divParent provided to the function. The Circle component is instantiated with three props: color, diameter and onClick. The first two are passed down by the renderCircle function. The onClick callback when triggered from the React component will call the _handleClickCircle function with the first argument being the div element where we render the Circle component and the second being the the date coming from the React component.

For a more generic form we could have use the ES6 rest parameters like so : onClick={…args => _handleClickCircle(parentDiv, …args)}

We will come back to what is happening inside _handleClickCircle after we look at the GWT Widget.

And finally we will create the GWT Widget that will instantiate and control the React Circle component

Let’s see what is happening here.

First we create a Class called ShapeViewerWidget which is derived from com.google.gwt.user.client.ui.Widget (Widgets are the GWT pieces to construct user interfaces). When we instantiate a ShapeViewerWidget then we will call the constructor. In the constructor we create two div elements, one will be the main container and the second one, which is appended to the main container, will be the one used to inject/unmount our React component and also listen/dispatch events. Actually because we will use this element everywhere in the class we save it as a class property circleReactElement.

Widget provides two methods onLoad() and onUnload(). We can think of theses as equivalent in React as render() and componentWillUnmount(). When the Widget object is instantiated, GWT call onLoad() and at this moment we can render our React component and add an event listener (We will come back to the event listener in a minute).

To render the React component we call the method renderCircle and pass the div element where our component will be rendered in and also a color and a diameter. Looking more closely the method renderCircle has a funny look :

/* -{$wnd.ShapeViewer.renderReactCircle(element, color, diameter)}-*/

It’s actually JavaScript code inside a Java class. There is a feature in GWT called JSNI that provides a special syntax to inject JS code into Java. When GWT compiler sees that syntax it will put the code in generated JS code. More info can be found here : http://www.gwtproject.org/doc/latest/DevGuideCodingBasicsJSNI.html

These wrapped Java methods are the key to bridge Java and JS and in our case GWT and React.

Inside this /*- … -*/ we can add any JS code that we like wrapped around a native method. Here the keyword native in the signature, which does not exist in Java, is coming from GWT and signal that the code inside the method will be native JS. Look at the debug method in our class for instance :

private static native void debug(String msg)/*-{ console.log(msg); }-*/;

Coming back to what’s inside renderCircle we see $wnd which is the global window object. If you recall, in the middleware we injected ShapeViewer object that exposes 2 functions, here we are using renderReactCircle and pass down the element where we want to render the React component and the 2 props color and diameter.

We covered one side of the story on how to render a React component from a GWT container. We want now to pass data from the React component up to the GWT container.

To do that it’s simple we will use the Event mechanism provided by JavaScript https://developer.mozilla.org/en-US/docs/Web/Guide/Events/Creating_and_triggering_events

In JavaScript we can create an event, dispatch and listen to it through a DOM element.

For our example we will use an event called onClickCircle. the GWT Widget will listen to it and call its own onClickOnCircleEvent method when the event is triggered which will display the date in the console.

addClickOnCircleEventListener register a listener to onClickCircle event into the div element where the React is rendered thanks to addEventListener which takes two arguments : the event to listen to and the callback to execute when the event is triggered.

So our GWT Widget is listening an event, we need now to create and dispatch this event. This action is done in the middleware code. Remember the callback _handleClickCircle that is triggered by the React onClick props :

In this function we create a CustomEvent with an event name (onClickCircle) and passing down a detail object containing our clickedDate value coming from the React component. Once the event is created, we just need to dispatch it from an element (here we need the element that rendered our Circle component) using the dispatchEvent function.

And that’s it.

Quick note on the Java class that transforms the JavaScript date send by the Circle component into a Java Object that we called ReactEventDetail.java.

We can see again some JSNI syntax here. We take the detail object send by onClickCircle event containing clickedDate which we return into the ReactEventDetail getter method. ReactEventDetail is passed by the listener callback and we can access the object getDate() method which will return the date string. It is possible to return more complex types like actual objects using the class com.google.gwt.core.client.JavaScriptObject.

Note that in this example we just use a simple component but it’s possible to use more complex components that can be updated, manage there own Redux store, call endpoints etc. And by upgrading your app you will ultimately create more and more complex components to replace the legacy features. The principle described here will be the same.

Conclusion

Integrating React components into Java legacy code can be a little hacky. By creating a middleware between the Java and the JavaScript code we separate the concern on how the component is created and how the data is flowing up and down. The day the legacy code will be deprecated it will be easy to just remove the middleware and create a React container to replace it. Hopefully when this day comes React won’t be already obsolete.

--

--

Julien Sanmartin

Front-end developer passionate about JavaScript, React and CSS