User 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.
| Both sides previous revisionPrevious revisionNext revision | Previous revision | ||
| development:applications:reactjsreduxgwt [2017/10/12 09:27] – wlee | development: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( | ||
| + | < | ||
| + | <App /> | ||
| + | </ | ||
| + | document.getElementById(' | ||
| + | ); | ||
| + | </ | ||
| + | |||
| + | 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( | ||
| + | < | ||
| + | <App /> | ||
| + | </ | ||
| + | document.getElementById(selector) | ||
| + | ); | ||
| + | } | ||
| + | |||
| + | module.exports = { | ||
| + | renderComponent: | ||
| + | }; | ||
| + | window.renderComponent= renderComponent; | ||
| + | </ | ||
| + | |||
| + | Simply build js file (bundle.js etc...) and put it to your GWT project. | ||
| + | |||
| + | For SnmpManager, | ||
| + | |||
| + | 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(" | ||
| + | add(label); | ||
| + | } | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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(" | ||
| + | } | ||
| + | |||
| + | public native void renderHostTreeReact(String containerId) /*-{ | ||
| + | | ||
| + | }-*/; | ||
| + | </ | ||
| + | |||
| + | You need to put render method in onLoad() method otherwise React complains there is no such element with ID " | ||
| + | |||
| + | This is all the work you need to put React App in GWT. | ||
| + | |||
| + | If your React app with Redux, nothing shouldn' | ||
| + | |||
| + | ====== 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 | ||
| + | {{: | ||
| + | |||
| + | Here is sequence diagram. | ||
| + | {{: | ||
| + | |||
| + | |||
| + | ===== Dispatching Redux Action from GWT ===== | ||
| + | In index.js of ReactRedux App, add following codes. | ||
| + | |||
| + | <code javascript> | ||
| + | // index.js | ||
| + | |||
| + | import * as bitcoinActions from ' | ||
| + | export function fetchBitcoinPrice(code) { | ||
| + | store.dispatch(bitcoinActions.fetchBitcoinPrice(code)) | ||
| + | } | ||
| + | |||
| + | module.exports = { | ||
| + | fetchBitcoinPrice: | ||
| + | renderComponent: | ||
| + | }; | ||
| + | window.fetchBitcoinPrice= fetchBitcoinPrice; | ||
| + | window.renderComponent= renderComponent; | ||
| + | </ | ||
| + | |||
| + | 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(" | ||
| + | }-*/; | ||
| + | </ | ||
| + | |||
| + | 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, | ||
| + | if (rateCodeFunc === undefined) { | ||
| + | console.error(" | ||
| + | } else { | ||
| + | console.log(" | ||
| + | console.log(rate + " and " + code); | ||
| + | rateCodeFunc(gwtComp, | ||
| + | } | ||
| + | } | ||
| + | } | ||
| + | |||
| + | export default GwtHolder | ||
| + | </ | ||
| + | |||
| + | 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.rateCodeFunc(rate, | ||
| + | </ | ||
| + | |||
| + | 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: | ||
| + | renderComponent: | ||
| + | GwtHolder: GwtHolder, | ||
| + | }; | ||
| + | window.fetchBitcoinPrice= fetchBitcoinPrice; | ||
| + | window.renderComponent= renderComponent; | ||
| + | window.GwtHolder= GwtHolder; | ||
| + | </ | ||
| + | |||
| + | |||
| + | ==== 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:: | ||
| + | }-*/; | ||
| + | | ||
| + | public void gwtAnnounce(String message) { | ||
| + | WindowDialog.notify(message); | ||
| + | } | ||
| + | </ | ||
| + | |||
| + | 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, " | ||
| + | |||
| + | 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:: | ||
| + | |||
| + | $wnd.renderComponent(containerId); | ||
| + | $wnd.GwtHolder.setGwtComp(reactPanel); | ||
| + | $wnd.GwtHolder.setRateCodeFunc(func); | ||
| + | }-*/; | ||
| + | </ | ||
| + | |||
| + | ==== Why not " | ||
| + | If you write GWT native method to be given to React App without argument " | ||
| + | <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:: | ||
| + | }-*/; | ||
| + | |||
| + | public void gwtAnnounce(String message) { | ||
| + | WindowDialog.notify(message); | ||
| + | } | ||
| + | </ | ||
| + | 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 **" | ||
| + | |||
| + | If you have look at console log before the error, you'll see javascript object " | ||
| + | |||
| + | This is why you have to take GWT component itself (For this page's example, " | ||
| + | |||
| + | 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:: | ||
| + | func.bind(this); | ||
| + | |||
| + | $wnd.renderComponent(containerId); | ||
| + | $wnd.GwtHolder.setRateCodeFunc(func); | ||
| + | }-*/; | ||
| + | </ | ||
| + | |||
| + | ==== Why not simply gwtComp.function() ==== | ||
| + | <code javascript> | ||
| + | import GwtHolder from ' | ||
| + | GwtHolder.getGwtComp().price(rate, | ||
| + | </ | ||
| + | |||
| + | This will give you " | ||
| + | |||
| + | I suspect we need to take some extra step to call GWT native method directly from javascript. | ||