Trying to use JsInterop to work with React

733 views
Skip to first unread message

Paul Stockley

unread,
Mar 13, 2016, 4:35:30 PM3/13/16
to GWT Contributors
We have started using React (using ES6 and FlowTypes) which is quite nice. However, we have a large GWT application that we want to start embedding React within. This makes perfect sense for migrating away from Widgets to a more modern approach. So I decided I would try and define a Java Api for React. After a couple of hours I got a really hacky prototype basically working. The code looks like

private ClassicComponentClass<BaseProps> customComponent;

public void onModuleLoad() {

customComponent = React.createClass(new CustomComponent());

HTMLProps props = Props.newHTML();
props.setDefaultValue("Test");

DOMElement<HTMLProps> div =
React.createElement("div", props,
React.createElement("div", null),
React.createElement(customComponent, null),
React.createElement("div", null, "An example")
);

ReactDOM.render(div, Document.get().getElementById("mainCont"));
}

@JsType(namespace = JsPackage.GLOBAL, name="CustomComponent")
public class CustomComponent {

@JsMethod
public ReactElement<?> render() {
return React.createElement("div", null, "It works");
}
}


This works fine for intrinsic React components. The problem is defining custom components. The React.createClass function needs to take a plain javascript object with certain methods defined (render, lifecyle methods etc). If I pass a Java object marked as JsType, this doesn't work because the render method needs to be defined on the top level object. The code puts it on a different prototype which doesn't work because React does the following:


  for (var name in spec) {
    if (!spec.hasOwnProperty(name)) {  --Isn't true for render
      continue;
    }

Is there some way to achieve what I want with JsInterop?  I tried extending another base class marked with isNative=true. However, this results in a runtime error when trying to construct an object of that type.

I am going to try a real hack with a JSNI method that constructs an object given the various
methods as parameters.


Colin Alworth

unread,
Mar 13, 2016, 5:12:57 PM3/13/16
to GWT Contributors
Yikes, that's rather opinionated. I don't personally work with React, but I guess I'm surprised that it can't handle actual inheritance (or even defining a prototype and attaching it to a constructor). 

Based on what you have there and the details described at https://facebook.github.io/react/docs/top-level-api.html, I'd suspect that they actually want the equivalent of the class object itself, not an instance of it (the line between them is blury in JS already). 

> One thing that makes components different than standard prototypal classes is that you don't need to call new on them. They are convenience wrappers that construct backing instances (via new) for you.

This seems to imply that you can't be guaranteed that your constructors will actually be called, but also reinforces the idea that they don't want you to pass in a class instance, just the methods that should be called on the eventually-created instances. More details from https://facebook.github.io/react/docs/component-specs.html seem to say that you are actually expected to pass in just a bag of properties, one of which will be that `render` function, which can trust that a `this` will be specified to provide `props` and `state`.

If there is a hook that lets you define your own constructor or factory method, that would be good to have to correctly do the GWT object wiring (perhaps some jsinterop expert can pipe up here as to what will be lost without that being called). Otherwise, some jsni or JsObject-building code that copies references to functions over to the a map-like object would be ideal. A JsProperty-annotated field may be enough to convince the compiler to generate correct code inside your function to access those properties, but I would be less sure about non-static helper methods still being around in the generated component.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/1dfb2508-9f88-4ecc-a30f-51814c496082%40googlegroups.com.
For more options, visit https://groups.google.com/d/optout.

Goktug Gokdogan

unread,
Mar 13, 2016, 5:32:19 PM3/13/16
to google-web-toolkit-contributors
See examples on how ES6 classes work with React. Those examples should apply to @JsTypes as well.

Paul Stockley

unread,
Mar 13, 2016, 5:45:48 PM3/13/16
to GWT Contributors
I did think about the ES6 class approach. However, The createElement method would need to take the class as a parameter (not an instance). How would I do that?
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

James Horsley

unread,
Mar 13, 2016, 5:49:31 PM3/13/16
to GWT Contributors
Paul, any chance you're thinking of releasing your code on github or something? I think an open sourced ReactJs-GWT jsinterop wrapper would be something a number of people might be interested in using and contributing to.

To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/186118a0-e595-47ea-a98a-24ca8603535f%40googlegroups.com.

Paul Stockley

unread,
Mar 13, 2016, 6:45:18 PM3/13/16
to GWT Contributors
If I can get a fully working proof of concept I may consider creating a project on github. However, there is still a lot to prove before I get to that stage. 

Paul Stockley

unread,
Mar 13, 2016, 8:21:57 PM3/13/16
to GWT Contributors
I got the following working. Seems like there should be a cleaner way. 

@JsType
public class CustomComponent {

public static ReactElement<?> render() {

return React.createElement("div", null, "It works");
}

    static final public native JavaScriptObject makeSpec() /*-{
return { render : $entry(@com.ocs.react.client.CustomComponent::render()) };
}-*/;
}

public class ReactMain implements EntryPoint {

@Override
public void onModuleLoad() {

ClassicComponentClass<BaseProps> customComponent = React.createClass(CustomComponent.makeSpec());

Goktug Gokdogan

unread,
Mar 14, 2016, 2:26:46 AM3/14/16
to google-web-toolkit-contributors
I'm not knowledgeable about React but I assume that you cannot instantiate a React component directly via its constructor, instead you need to call another function and pass the class?

The class in javascript is actually the "constructor" function. There are multiple ways to get the constructor function in GWT;

1) Use JSNI. e.g.
  Function getMyConstructor() /*-{
    return MyComponent::new(*);
  }-*/;

2) Use JsInterop. 

  @JsProperty(namespace=<JsNamespaceForTheComponent>)
  native Function getMyComponent();

(Requires you to enable JsInterop exports -generateJsInteropExports)

3) Create a generic JSNI utility:

  native Function getConstructor(String qualifiedJsTypeName) /*-{
    var cur = $wnd;
    var parts = namespace.split('.');
    for (var part; parts.length && (part = parts.shift());) {
        cur = cur[part];
    }
    return cur;
  }-*/;

Then use as getConstructor("<JsNamespaceForTheComponent>.MyComponent")

(Requires you to enable JsInterop exports -generateJsInteropExports)

4) A fragile JSNI hack that may break in any version:

  native Function getConstructor(Class<?> clazz) /*-{
    return @Class::getPrototypeForClass(*)(clazz).constructor;
  }-*/;

Then use as getConstructor(MyComponent.class).



To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/186118a0-e595-47ea-a98a-24ca8603535f%40googlegroups.com.

Rene Hangstrup Møller

unread,
Mar 14, 2016, 4:20:30 AM3/14/16
to GWT Contributors
This is very interesting. We are also planning to make the move from GWT to Typescript and React. This might be useful for the transition period. The ambient Typescript definitions for React may also prove useful when defining the JsInterop types.

Best regards
Rene
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

Paul Stockley

unread,
Mar 14, 2016, 11:08:51 AM3/14/16
to GWT Contributors
Thanks. I will give this a try. This information would be useful to add to any official JsInterop documentation. 
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

Paul Stockley

unread,
Mar 15, 2016, 11:12:18 PM3/15/16
to GWT Contributors
I didn't have any success with any of the methods you presented. See below

import java.util.function.Function;


@JsType(namespace = JsPackage.GLOBAL, name="CustomComponent")
public class CustomComponent extends React.Component {
public BaseProps props;

public ReactElement<?> render() {
return React.createElement("div", null, "Classic Comp works");
}

// 1) Just returns undefined
@JsProperty(namespace = JsPackage.GLOBAL)
public static native Function getCustomComponent();

// 2) Crashes compiler with internal error
public static native Function constructor() /*-{
return @com.ocs.react.client.CustomComponent::new(*);
}-*/;

// 3) Crashes compiler with internal error
public static native Function constructor(String qualifiedJsTypeName) /*-{

Paul Stockley

unread,
Mar 15, 2016, 11:26:26 PM3/15/16
to GWT Contributors
I have made a bit more progress. I managed to get functional components working. 

@JsType(isNative = true)
private static class FuncProps extends BaseProps {
public String aProp;
}

static final public native FuncProps makeFuncProps() /*-{
return {};
}-*/;

@Override
public void onModuleLoad() {
ClassicComponentClass<BaseProps> customClassicComponent = React.createClass(CustomClassicComponent.makeSpec());

FunctionalComponent<FuncProps> funcComp =
(props)->{return React.createElement("div", null, "Functional component works " + props.aProp);};

FuncProps fProps = makeFuncProps();
fProps.aProp = "Its a prop";

DOMElement<HTMLProps> div =
React.createElement("div", Props.HTML()._defaultValue("ss")._accept("y"),
React.createElement("div", null,
React.createElement(funcComp, fProps)),
React.createElement(customClassicComponent, null),

React.createElement("div", null, "An example")
);

ReactDOM.render(div, Document.get().getElementById("mainCont"));
}


So far no luck with ES6 based components. Javascript object literals are a real pain to deal with in Java at the moment. I would like to be able to do "new FuncProps()" but it fails at runtime. So I have to resort to using JSNI to create the object. Having some sort of fluent interface like I defined for HTML props is about as close as you will get to the convenience of object literals in java. I could define a very generic javascript object like a map. This would be flexible but not very type safe. 

Goktug Gokdogan

unread,
Mar 16, 2016, 11:03:28 AM3/16/16
to google-web-toolkit-contributors
On Tue, Mar 15, 2016 at 8:12 PM, Paul Stockley <pstoc...@gmail.com> wrote:
I didn't have any success with any of the methods you presented. See below

import java.util.function.Function;

@JsType(namespace = JsPackage.GLOBAL, name="CustomComponent")

You don't need provide name when it is same as the original name,
 
public class CustomComponent extends React.Component {
public BaseProps props;

public ReactElement<?> render() {
return React.createElement("div", null, "Classic Comp works");
}

// 1) Just returns undefined
@JsProperty(namespace = JsPackage.GLOBAL)
public static native Function getCustomComponent();

This should work accept that "java.util.function.Function" is not a JavaScript function. Replace that with JavaScriptObject or a JsType that maps to JavaScript function.
However, regardless you should not be getting 'undefined' unless you didn't pass the "--generateJsInteropExport true" flag. If you are using RC release, try switching to a snapshot release.
 
    // 2) Crashes compiler with internal error
Can you provide mode detail on error?
 
    public static native Function constructor() /*-{
return @com.ocs.react.client.CustomComponent::new(*);
}-*/;

// 3) Crashes compiler with internal error
Can you provide mode detail on error? 
    public static native Function constructor(String qualifiedJsTypeName) /*-{
var cur = $wnd;
var parts = namespace.split('.');
for (var part; parts.length && (part = parts.shift());) {
cur = cur[part];
}
return cur;
}-*/;
}


--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

Goktug Gokdogan

unread,
Mar 16, 2016, 11:10:53 AM3/16/16
to google-web-toolkit-contributors
On Tue, Mar 15, 2016 at 8:26 PM, Paul Stockley <pstoc...@gmail.com> wrote:
I have made a bit more progress. I managed to get functional components working. 

@JsType(isNative = true)
private static class FuncProps extends BaseProps {
public String aProp;
}

static final public native FuncProps makeFuncProps() /*-{
return {};
}-*/;

@Override
public void onModuleLoad() {
ClassicComponentClass<BaseProps> customClassicComponent = React.createClass(CustomClassicComponent.makeSpec());

FunctionalComponent<FuncProps> funcComp =
(props)->{return React.createElement("div", null, "Functional component works " + props.aProp);};

FuncProps fProps = makeFuncProps();
fProps.aProp = "Its a prop";

DOMElement<HTMLProps> div =
React.createElement("div", Props.HTML()._defaultValue("ss")._accept("y"),
React.createElement("div", null,
React.createElement(funcComp, fProps)),
React.createElement(customClassicComponent, null),
React.createElement("div", null, "An example")
);

ReactDOM.render(div, Document.get().getElementById("mainCont"));
}


So far no luck with ES6 based components. Javascript object literals are a real pain to deal with in Java at the moment. I would like to be able to do "new FuncProps()" but it fails at runtime.

It would fail because the compiler will generate "new some.package.name.FuncProps()" in JavaScript where "some.package.name.FuncProps" is undefined. Point it to Global "Object" so the generated code is "new Object" (which is same as "{}").
 
So I have to resort to using JSNI to create the object. Having some sort of fluent interface like I defined for HTML props is about as close as you will get to the convenience of object literals in java. I could define a very generic javascript object like a map. This would be flexible but not very type safe.

There is a design for making it more elegant to work with such objects but that is not planned for 2.8 release.
 
 

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

Paul Stockley

unread,
Mar 16, 2016, 11:36:34 AM3/16/16
to GWT Contributors

Changing the class to 

@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")

private static class FuncProps extends BaseProps {
public String aProp;
}

Now makes new FuncProps() work, which is great.

Paul Stockley

unread,
Mar 16, 2016, 11:51:19 AM3/16/16
to GWT Contributors
I am running the latest snapshot build as of a couple of days ago and starting SD mode with

Running CodeServer with parameters: [-noprecompile, -port, 9876, -sourceLevel, 1.8, -bindAddress, 127.0.0.1, -launcherDir, /Users/paul/Library/Caches/IntelliJIdea15/gwt/gwt_react.da4c73b3/gwt_react.284d7c82/run/www, -logLevel, INFO, -generateJsInteropExports, -style, OBFUSCATED, com.ocs.react.React]

I changed the Class to the following as suggested but it still doesn't work

public class JSFunction extends JavaScriptObject {
protected JSFunction() {};
}

@JsType(namespace = JsPackage.GLOBAL, name="CustomComponent")
public class CustomComponent {

    // 1) Still Just returns undefined
@JsProperty(namespace = JsPackage.GLOBAL)
public static native JSFunction getCustomComponent();

// 2) Still crashes compiler with internal error
public static native JSFunction constructor() /*-{
return @com.ocs.react.client.CustomComponent::new(*);
}-*/;

// 3) Still crashes compiler with internal error
public static native JSFunction constructor(String qualifiedJsTypeName) /*-{
        var cur = $wnd;
var parts = namespace.split('.');
for (var part; parts.length && (part = parts.shift());) {
cur = cur[part];
}
return cur;
}-*/;
}

The exception is

            [ERROR] An internal compiler exception occurred
com.google.gwt.dev.jjs.InternalCompilerException: Unexpected error during visit.
at com.google.gwt.dev.jjs.ast.JVisitor.translateException(JVisitor.java:111)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:149)
at com.google.gwt.dev.jjs.ast.JVisitor.acceptImmutable(JVisitor.java:154)
at com.google.gwt.dev.jjs.ast.JMethodCall.visitChildren(JMethodCall.java:266)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:257)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:146)
at com.google.gwt.dev.jjs.ast.JVisitor.acceptImmutable(JVisitor.java:154)
at com.google.gwt.dev.jjs.ast.JMethodCall.visitChildren(JMethodCall.java:266)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:257)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:146)
at com.google.gwt.dev.jjs.ast.JVisitor.acceptImmutable(JVisitor.java:154)
at com.google.gwt.dev.jjs.ast.JMethodCall.visitChildren(JMethodCall.java:266)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:257)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:118)
at com.google.gwt.dev.jjs.ast.JDeclarationStatement.traverse(JDeclarationStatement.java:49)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:146)
at com.google.gwt.dev.jjs.ast.JVisitor.acceptWithInsertRemove(JVisitor.java:168)
at com.google.gwt.dev.jjs.ast.JBlock.traverse(JBlock.java:92)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:139)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:135)
at com.google.gwt.dev.jjs.ast.JMethodBody.traverse(JMethodBody.java:83)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
at com.google.gwt.dev.jjs.ast.JMethod.visitChildren(JMethod.java:788)
at com.google.gwt.dev.jjs.ast.JMethod.traverse(JMethod.java:780)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:146)
at com.google.gwt.dev.jjs.ast.JVisitor.acceptWithInsertRemoveImmutable(JVisitor.java:172)
at com.google.gwt.dev.jjs.ast.JClassType.traverse(JClassType.java:147)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
at com.google.gwt.dev.jjs.ast.JProgram.visitModuleTypes(JProgram.java:1284)
at com.google.gwt.dev.jjs.ast.JProgram.traverse(JProgram.java:1249)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:127)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:122)
at com.google.gwt.dev.jjs.impl.ControlFlowRecorder.execImpl(ControlFlowRecorder.java:162)
at com.google.gwt.dev.jjs.impl.ControlFlowRecorder.exec(ControlFlowRecorder.java:49)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.maybeRecordReferencesAndControlFlow(JavaToJavaScriptCompiler.java:474)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:352)
at com.google.gwt.dev.jjs.JavaToJavaScriptCompiler.compilePermutation(JavaToJavaScriptCompiler.java:272)
at com.google.gwt.dev.CompilePerms.compile(CompilePerms.java:198)
at com.google.gwt.dev.ThreadedPermutationWorkerFactory$ThreadedPermutationWorker.compile(ThreadedPermutationWorkerFactory.java:50)
at com.google.gwt.dev.PermutationWorkerFactory$Manager$WorkerThread.run(PermutationWorkerFactory.java:74)
at java.lang.Thread.run(Thread.java:745)
Caused by: java.lang.NullPointerException
at com.google.gwt.dev.jjs.ast.JMethod.getJsniSignature(JMethod.java:618)
at com.google.gwt.dev.jjs.impl.ControlFlowRecorder.computeName(ControlFlowRecorder.java:53)
at com.google.gwt.dev.jjs.impl.ControlFlowRecorder.processMethodCall(ControlFlowRecorder.java:212)
at com.google.gwt.dev.jjs.impl.ControlFlowRecorder.visit(ControlFlowRecorder.java:157)
at com.google.gwt.dev.jjs.ast.JMethodCall.traverse(JMethodCall.java:256)
at com.google.gwt.dev.jjs.ast.JVisitor.accept(JVisitor.java:146)
... 44 more
               [ERROR] at ReactMain.java(38): CustomComponent.getCustomComponent()
                  com.google.gwt.dev.jjs.ast.JMethodCall
               [ERROR] at ReactMain.java(38): React.createElement(CustomComponent.getCustomComponent(), null)
                  com.google.gwt.dev.jjs.ast.JMethodCall
               [ERROR] at ReactMain.java(36): React.createElement("div", null, React.createElement(funcComp, fProps), React.createElement(CustomComponent.getCustomComponent(), null))
                  com.google.gwt.dev.jjs.ast.JMethodCall
               [ERROR] at ReactMain.java(35): React.createElement("div", (new HTMLProps())._defaultValue("ss")._accept("y"), React.createElement("div", null, React.createElement(funcComp, fProps), React.createElement(CustomComponent.getCustomComponent(), null)), React.createElement(customClassicComponent, null), React.createElement("div", null, "An example"))
                  com.google.gwt.dev.jjs.ast.JMethodCall
               [ERROR] at ReactMain.java(34): DOMElement div = React.createElement("div", (new HTMLProps())._defaultValue("ss")._accept("y"), React.createElement("div", null, React.createElement(funcComp, fProps), React.createElement(CustomComponent.getCustomComponent(), null)), React.createElement(customClassicComponent, null), React.createElement("div", null, "An example"))
                  com.google.gwt.dev.jjs.ast.JDeclarationStatement
               [ERROR] at ReactMain.java(24): {
  ClassicComponentClass customClassicComponent = React.createClass(CustomClassicComponent.makeSpec());
  FunctionalComponent funcComp = new ReactMain$lambda$0$Type();
  ReactMain$FuncProps fProps = new ReactMain$FuncProps();
  fProps.aProp = "Its a prop";
  DOMElement div = React.createElement("div", (new HTMLProps())._defaultValue("ss")._accept("y"), React.createElement("div", null, React.createElement(funcComp, fProps), React.createElement(CustomComponent.getCustomComponent(), null)), React.createElement(customClassicComponent, null), React.createElement("div", null, "An example"));
  ReactDOM.render(div, Document.get().getElementById("mainCont"));
}
                  com.google.gwt.dev.jjs.ast.JBlock
               [ERROR] at ReactMain.java(24): {
  ClassicComponentClass customClassicComponent = React.createClass(CustomClassicComponent.makeSpec());
  FunctionalComponent funcComp = new ReactMain$lambda$0$Type();
  ReactMain$FuncProps fProps = new ReactMain$FuncProps();
  fProps.aProp = "Its a prop";
  DOMElement div = React.createElement("div", (new HTMLProps())._defaultValue("ss")._accept("y"), React.createElement("div", null, React.createElement(funcComp, fProps), React.createElement(CustomComponent.getCustomComponent(), null)), React.createElement(customClassicComponent, null), React.createElement("div", null, "An example"));
  ReactDOM.render(div, Document.get().getElementById("mainCont"));
}
                  com.google.gwt.dev.jjs.ast.JMethodBody
               [ERROR] at ReactMain.java(24): com.ocs.react.client.ReactMain.onModuleLoad()V
                  com.google.gwt.dev.jjs.ast.JMethod
               [ERROR] at ReactMain.java(16): com.ocs.react.client.ReactMain (extends Object implements EntryPoint)
                  com.google.gwt.dev.jjs.ast.JClassType
               [ERROR] at Unknown(0): <JProgram>
                  com.google.gwt.dev.jjs.ast.JProgram
            [ERROR] Unrecoverable exception, shutting down
com.google.gwt.core.ext.UnableToCompleteException: (see previous log entries)
at com.google.gwt.dev.javac.CompilationProblemReporter.logAndTranslateException(CompilationProblemReporter.java:106)



 

Goktug Gokdogan

unread,
Mar 16, 2016, 12:05:30 PM3/16/16
to google-web-toolkit-contributors
On Wed, Mar 16, 2016 at 8:51 AM, Paul Stockley <pstoc...@gmail.com> wrote:
I am running the latest snapshot build as of a couple of days ago and starting SD mode with

Running CodeServer with parameters: [-noprecompile, -port, 9876, -sourceLevel, 1.8, -bindAddress, 127.0.0.1, -launcherDir, /Users/paul/Library/Caches/IntelliJIdea15/gwt/gwt_react.da4c73b3/gwt_react.284d7c82/run/www, -logLevel, INFO, -generateJsInteropExports, -style, OBFUSCATED, com.ocs.react.React]

I changed the Class to the following as suggested but it still doesn't work

public class JSFunction extends JavaScriptObject {
protected JSFunction() {};
}

@JsType(namespace = JsPackage.GLOBAL, name="CustomComponent")
public class CustomComponent {

// 1) Still Just returns undefined
You declared JsFunction as JSO and it is never returned from JSNI so it is always considered to be null (I guess). Use @JsType(namespace=GLOBAL) interface JsFunction {} instead.
 
    @JsProperty(namespace = JsPackage.GLOBAL)
public static native JSFunction getCustomComponent();

// 2) Still crashes compiler with internal error
I think these are crashing the compiler because they are JsMethods (public methods in JsType). Can you try changing their visibility or moving to a different file that is not JsType?

Also can you file bug with the stack trace?

--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

Paul Stockley

unread,
Mar 16, 2016, 12:23:25 PM3/16/16
to GWT Contributors
I tried using a JsType instead of JavaScriptObject and it is still undefined. I have logged a ticket with the details.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-contributors+unsubscribe@googlegroups.com.

Paul Stockley

unread,
Mar 16, 2016, 8:16:46 PM3/16/16
to GWT Contributors
Well I think I have hit a roadblock with the current state of JsInterop. Given the code below

 
 public class Example {
    @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
    private static class State extends BaseState {
public String aStateVar;
}

static public BaseState getInitialState() {
State s = new State();
s.aStateVar = "Initial Value";
return s;
}

protected static final native <S extends BaseState> S getState() /*-{;
return this.state;
}-*/;

static public ReactElement<?> render() {

//getState will not work because the this in the global JSNI function is bound to $Wnd
State ourState = getState();

.
.

}

public native JavaScriptObject makeSpec() /*-{
return {
            render : $entry(this.render()),
getInitialState : $entry(this.getInitialState())
};
}-*/;

}

The render method gets called fine and within this method this points to the react element. I need to get the state and props that are stored as variables. The problem is I have no way of getting this.props unless I use JSNI. The JSNI function created is defined on the global window object and this is bound to the window object. So I am dead in the water until I can get the class based component working. Even if I get this working I will face another issue. In ES6 components you normally have to bind any callbacks to the enclosing object.this. e.g.


render() { return ( <div onClick={this.tick.bind(this)}> Clicks: {this.state.count} </div> ); 
} 


Goktug Gokdogan

unread,
Mar 18, 2016, 11:36:37 AM3/18/16
to google-web-toolkit-contributors
I'm sorry, I'm very busy right now, but I will take a look as soon as I can.

} 


--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/google-web-toolkit-contributors/ac1fbf90-4b52-4a2e-9224-c4b16a218e17%40googlegroups.com.
Message has been deleted

James Horsley

unread,
Mar 18, 2016, 12:24:30 PM3/18/16
to GWT Contributors
Paul, brilliant news. Look forward to hopefully taking a look at your code if you're able to share.

On Fri, Mar 18, 2016 at 4:15 PM Paul Stockley <pstoc...@gmail.com> wrote:
Success! With some javascript hacking I have managed to get it working. I can access state and props and set state as a result of an onClick event. I think it might be possible to get react fully working. Below is my somewhat contrived solution


--
You received this message because you are subscribed to the Google Groups "GWT Contributors" group.
To unsubscribe from this group and stop receiving emails from it, send an email to google-web-toolkit-co...@googlegroups.com.

Paul Stockley

unread,
Mar 18, 2016, 12:24:40 PM3/18/16
to GWT Contributors
Success! With some javascript hacking I have managed to get it working. I can retrieve props and state and also set state from an onClick callback. Below is my contrived solution

@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
public class AbstractClassicComponent {

protected native void setState(BaseState state);

protected native void setState(BaseState state, ReactCallback callback);

protected native void forceUpdate(ReactCallback callBack);

protected native void replaceState(BaseState nextState, ReactCallback callback);

protected native boolean isMounted();

protected native <P extends BaseProps> P getProps();

protected native <S extends BaseState> S getState();
}

@JsType(namespace = JsPackage.GLOBAL)
public class CustomClassicComponent extends AbstractClassicComponent {


@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
    public static class Props extends BaseProps {
public String aProp;
}

@JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
private static class State extends BaseState {
public String aStateVar;
}

    public BaseState getInitialState() {
State s = new State();
s.aStateVar = "Initial Value";
return s;
}

    public ReactElement<?> render() {
Props ourProps = getProps();
State ourState = getState();
String description = "Click me (state=" + ourState.aStateVar + ", props=" + ourProps.aProp + ")";

MouseEventHandler clickedBtn = (e) -> {
State newState = new State();
newState.aStateVar = "Updated Value";

setState(newState);
};

return React.createElement("button", new HTMLProps().Title("Some title").OnClick(clickedBtn), description);
}
}

public class ReactMain implements EntryPoint {

    @JsType(isNative = true, namespace = JsPackage.GLOBAL, name = "Object")
    private static class StatelessProps extends BaseProps {
public String aProp;
}

@Override
public void onModuleLoad() {
initJS();

ClassicComponentClass<CustomClassicComponent.Props> customClassicComponent =
React.createClass(GWTReact.makeSpec(new CustomClassicComponent()));

StatelessComponent<StatelessProps> statelessComp =
(props)->{return React.createElement("div", null, "Stateless component works " + props.aProp);};


StatelessProps fProps = new StatelessProps();
fProps.aProp = "Its a func prop";

CustomClassicComponent.Props classicProps= new CustomClassicComponent.Props();
classicProps.aProp = "classic prop";

DOMElement<HTMLProps> div =
React.createElement("div", new HTMLProps().DefaultValue("ss"),
React.createElement("div", null,
React.createElement(statelessComp, fProps)),
React.createElement(customClassicComponent, classicProps),

React.createElement("div", null, "An example")
);

ReactDOM.render(div, Document.get().getElementById("mainCont"));
}

    private final native void initJS() /*-{
$wnd.GWTReact = {};

$wnd.GWTReact.makeSpec = function(componentObj) {
var prototype = componentObj.__proto__;
var spec = {
getState: function() {
return this.state;
}
,getProps: function() {
return this.props;
}
};

for (var p in prototype) {

if (prototype.hasOwnProperty(p) && p != 'constructor') {
spec[p] = prototype[p];
}
}
return spec;
}
}-*/;
}

The trick is the makeSpec function. I am going to work fleshing out the API so I can build a real test application. I can also improve how you work with state and props.

Paul Stockley

unread,
Mar 21, 2016, 9:31:56 AM3/21/16
to GWT Contributors
I am making decent progress refining the API and making it more usable. I have decided to do a direct port 
 of http://todomvc.com/examples/react/#/. One of the reasons I chose this was so I could have a direct comparison
of  the Javsascript vs Java code. I am about half way done. The java code may actually end up less lines of code  which is
ironic given how Java is supposed to be a super verbose language. I will probably move this thread over to the G+ 
user group as I think the information will be useful to the general GWT user base.
Reply all
Reply to author
Forward
0 new messages