User Tools

Site Tools


Writing /app/www/public/data/meta/development/applications/reactjsreduxgwt.meta failed
development:applications:reactjsreduxgwt

Differences

This shows you the differences between two versions of the page.

Link to this comparison view

Both sides previous revisionPrevious revision
Next revision
Previous revision
development:applications:reactjsreduxgwt [2017/10/12 09:08] – [GWT Side] wleedevelopment:applications:reactjsreduxgwt [2021/06/25 10:09] (current) – external edit 127.0.0.1
Line 1: Line 1:
 +====== Render React App on GWT ======
 +===== React Side =====
 +Normal React App will look like this in index.js
 +<code javascript>
 +// index.js
  
 +ReactDOM.render(
 +  <Provider store={store}>
 +    <App />
 +  </Provider>,
 +  document.getElementById('root')
 +);
 +</code>
 +
 +You need to make following changes.
 +
 +  * Surround it in function clause
 +  * Export the function as module
 +  * Assign module function to window property
 +
 +<code javascript>
 +// index.js
 +
 +export function renderComponent(selector) {
 +  ReactDOM.render(
 +    <Provider store={store}>
 +      <App />
 +    </Provider>,
 +    document.getElementById(selector)
 +  );
 +}
 +
 +module.exports = {
 +  renderComponent: renderComponent,
 +};
 +window.renderComponent= renderComponent;
 +</code>
 +
 +Simply build js file (bundle.js etc...) and put it to your GWT project.
 +
 +For SnmpManager, put it in snmpmanager/web-app/js
 +
 +Load JavaScript file in gsp header before GWT.
 +
 +===== GWT Side =====
 +In constructor of GWT Component (such as Panel), add some child component with distinctive ID.
 +
 +<code java>
 +// ReactPanel.java
 +
 +public class ReactPanel extends VerticalPanel {
 +    public ReactPanel() {
 +        Label label = new Label();
 +        label.getElement().setId("react-hostTree-root");
 +        add(label);
 +    }
 +}
 +</code>
 +
 +In the same class, override onLoad() method and create native method which calls renderComponent() function of React App you just made above.
 +
 +<code java>
 +// ReactPanel.java
 +
 +    @Override
 +    protected void onLoad() {
 +        super.onLoad();
 +        renderHostTreeReact("react-hostTree-root");
 +    }
 +
 +    public native void renderHostTreeReact(String containerId) /*-{
 +     $wnd.renderComponent(containerId);
 +    }-*/;
 +</code>
 +
 +You need to put render method in onLoad() method otherwise React complains there is no such element with ID "react-hostTree-root". GWT doesn't necessary show all component in the browser when constructor is called.
 +
 +This is all the work you need to put React App in GWT.
 +
 +If your React app with Redux, nothing shouldn't be different. Everything should work in the same way as you run ReactRedux App with nodeJS.
 +
 +====== Interaction between GWT and ReactRedux ======
 +We are going to need to create GwtHolder.js. This class allows React Application to call function (native method) in GWT.
 +
 +Here is object relationship
 +{{:development:applications:react_gwt_together_object_relationship.png?800|}}
 +
 +Here is sequence diagram.
 +{{:development:applications:react_gwt_together_sequence.png?800|}}
 +
 +
 +===== Dispatching Redux Action from GWT =====
 +In index.js of ReactRedux App, add following codes.
 +
 +<code javascript>
 +// index.js
 +
 +import * as bitcoinActions from './actions/bitcoinActions';
 +export function fetchBitcoinPrice(code) {
 +  store.dispatch(bitcoinActions.fetchBitcoinPrice(code))
 +}
 +
 +module.exports = {
 +  fetchBitcoinPrice: fetchBitcoinPrice,
 +  renderComponent: renderComponent,
 +};
 +window.fetchBitcoinPrice= fetchBitcoinPrice;
 +window.renderComponent= renderComponent;
 +</code>
 +
 +Above code basically exporting function that dispatch redux action.
 +
 +By that, we can call fetchBitcoinPrice(code) function from GWT native method as follows.
 +
 +<code java>
 +// ReactPanel.java
 +
 +    public native void reactAction() /*-{
 +      $wnd.fetchBitcoinPrice("EUR");
 +    }-*/;
 +</code>
 +
 +From GWT code, we can call reactAction() method just like other JAVA method in GWT.
 +
 +===== Invoke GWT method from React App =====
 +==== React Side ====
 +We create JavaScript class (GwtHolder.js) in React App to interface React and GWT.
 +
 +<code javascript>
 +// GwtHolder.js
 +
 +let gwtComp
 +let rateCodeFunc;
 +
 +class GwtHolder {
 +  constructor() {
 +  }
 +
 +  static setGwtComp(comp) {
 +    gwtComp = comp;
 +  }
 +
 +  static setRateCodeFunc(func) {
 +    rateCodeFunc = func;
 +  }
 +
 +  static rateCodeFunc(rate, code) {
 +    if (rateCodeFunc === undefined) {
 +      console.error("rateCodeFunc is undefined");
 +    } else {
 +      console.log("calling rateCodeFunc");
 +      console.log(rate + " and " + code);
 +      rateCodeFunc(gwtComp, rate, code);
 +    }
 +  }
 +}
 +
 +export default GwtHolder
 +</code>
 +
 +Feature of GwtHolder class is 
 +  * Static variable gwtComp to store JavaScript object which represents GWT component
 +  * Static variable rateCodeFunc to store function which is native method in GWT
 +
 +React App can all GWT native method through GwtHolder as shown below. 
 +
 +<code javascript>
 +import GwtHolder from '../GwtHolder';
 +GwtHolder.rateCodeFunc(rate, code);
 +</code>
 +
 +Export GwtHolder in index.js so that GWT can see it. Later GWT will inject component and functions into GwtHolder.js
 +
 +<code javascript>
 +// index.js
 +
 +module.exports = {
 +  fetchBitcoinPrice: fetchBitcoinPrice,
 +  renderComponent: renderComponent,
 +  GwtHolder: GwtHolder,
 +};
 +window.fetchBitcoinPrice= fetchBitcoinPrice;
 +window.renderComponent= renderComponent;
 +window.GwtHolder= GwtHolder;
 +</code>
 +
 +
 +==== GWT Side ====
 +In GWT, write native method (JavaScript function) to be passed to GwtHolder and JAVA method which actually do some work with GWT.
 +
 +<code java>
 +// ReactPanel.java
 +
 +    public native void price(ReactPanel reactPanel, String price, String code) /*-{
 +      var message = "Price given to GWT from React is " + price + " and code is " + code;
 +      console.log(message);
 +
 +      console.log(reactPanel);
 +      reactPanel.@com.errigal.ems.client.dashboard.panel.ReactPanel::gwtAnnounce(Ljava/lang/String;)(message);
 +    }-*/;
 +    
 +    public void gwtAnnounce(String message) {
 +        WindowDialog.notify(message);
 +    }
 +</code>
 +
 +So above code, native method price() will be passed to GwtHolder.js and bridge JavaScript and GWT.
 +
 +We need to accept GWT Component instance as one of parameters. GWT Component means the class where all Java Code we write is written. In this case ReactPanel.
 +
 +The reason is once price() native method is passed to React side, "this" keyword cannot be used because "this" means React App, not GWT component.
 +
 +gwtAnnounce() method is normal Java method. In this case, it will show popup with message in GWT app.
 +
 +So sequence of calling GWT method from React App will be
 +  - React App calls GwtHolder.rateCodeFunc()
 +  - GwtHolder.rateCodeFunc() adds gwtComp as parameter when it calls rateCodeFunc() which is ReactPanel.price() given from GWT.
 +  - ReactPanel.price() do its job and calls ReactPanel.gwtAnnounce().
 +
 +Now we have function (native method) to pass to React. 
 +So all we need to do is actually pass it on to React. 
 +We can do so by adding extra code in renderReact method.
 +
 +<code java>
 +// ReactPanel.java
 +
 +    public native void renderHostTreeReact(String containerId) /*-{
 +      var reactPanel = this;
 +      var func = reactPanel.@com.errigal.ems.client.dashboard.layouts.ReactPanel::price(Lcom/errigal/ems/client/dashboard/layouts/ReactPanel;Ljava/lang/String;Ljava/lang/String;);
 +
 +      $wnd.renderComponent(containerId);
 +      $wnd.GwtHolder.setGwtComp(reactPanel);
 +      $wnd.GwtHolder.setRateCodeFunc(func);
 +    }-*/;
 +</code>
 +
 +==== Why not "this" in native method for React ====
 +If you write GWT native method to be given to React App without argument "ReactPanel reactPanel" like below,
 +<code java>
 +// malfunction - ReactPanel.java
 + 
 +    public native void price(String price, String code) /*-{
 +      var message = "Price given to GWT from React is " + price + " and code is " + code;
 +      console.log(message);
 + 
 +      console.log(this);
 +      this.@com.errigal.ems.client.dashboard.panel.ReactPanel::gwtAnnounce(Ljava/lang/String;)(message);
 +    }-*/;
 + 
 +    public void gwtAnnounce(String message) {
 +        WindowDialog.notify(message);
 +    }
 +</code>
 +First, IntelliJ won't give you any error because syntax are perfect. IntelliJ recognise this.~~~.gwtAnnounce() method exists and executable from native method.
 +
 +But when it actually executed from React, browser console tells you **"gwtAnnounce is not function"** or **"no such property gwtAnnounce"**.
 +
 +If you have look at console log before the error, you'll see javascript object "Window" is outputted. That is React App object.
 +
 +This is why you have to take GWT component itself (For this page's example, "ReactPanel reactPanel") as one of arguments.
 +
 +Using bind method like below but no avail.
 +
 +<code java>
 +// malfunction - ReactPanel.java
 + 
 +    public native void renderHostTreeReact(String containerId) /*-{
 +      var reactPanel = this;
 +      var func = reactPanel.@com.errigal.ems.client.dashboard.layouts.ReactPanel::price(Ljava/lang/String;Ljava/lang/String;);
 +      func.bind(this);
 + 
 +      $wnd.renderComponent(containerId);
 +      $wnd.GwtHolder.setRateCodeFunc(func);
 +    }-*/;
 +</code>
 +
 +==== Why not simply gwtComp.function() ====
 +<code javascript>
 +import GwtHolder from '../GwtHolder';
 +GwtHolder.getGwtComp().price(rate, code);
 +</code>
 +
 +This will give you "Uncaught TypeError: a.price is not a function" in browser console log.
 +
 +I suspect we need to take some extra step to call GWT native method directly from javascript.