Fork us on GitHub

Java is Superior To React Native In Practically Every Way

I'm biased and yes, there is a personal taste aspect but having reviewed react native I don't see any benefit
Post Image

Java is Superior To React Native In Practically Every Way

I got into a discussion with a colleague on the Java vs. JavaScript subject, which is a problematic subject to begin with. He then mentioned how great React Native is, I decided I have to look into it and maybe grab some ideas for Codename One...
There are some nice ideas there, but none of them is revolutionary or exceptional and most of them are pretty old news for Codename One developers running in Java 8.

One thing I did like was how short the React demo code seemed to be, so I ported it to Codename One and ended up with roughly the same amount of code and arguably better/simpler code!
Check out the full listing at the end of the article or in the github project here, but lets first review why the Java code is "better".

Synchronous Execution

JavaScript fans hate this but its still a fact that synchronous code is simpler to read, follow and debug. E.g. this is the React Native version of the code that fetches the data:

fetchData: function() {
  fetch(REQUEST_URL) 
        .then((response) => response.json()) 
        .then((responseData) => { 
             this.setState({ 
                  dataSource: this.state.dataSource.cloneWithRows(responseData.movies), 
                  loaded: true, 
             }); 
         }) 
  .done(); 
},

I have well over 20 years of professional programming experience and this is still hard to follow. Apparently if done() is omitted you won't get any error handling?
Its weird and error prone. I feel like a lot of code is hidden behind this which makes the terseness more confusing than simplifying (kind of like following a political debate thru Twitter).
To me our code is way simpler:

react.add(BorderLayout.CENTER, new InfiniteContainer() {
    public Component[] fetchComponents(int index, int amount) {
        try {
            Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
            Component[] response = new Component[data.size()];
            int offset = 0;
            for(Object movie : data) {
                response[offset] = createMovieEntry(Result.fromContent((Map)movie));
                offset++;
            }
            return response;
        } catch(IOException err) {
            Dialog.show("Error", "Error during connection: " + err, "OK", null);
        }
        return null;
    }
});

Notice that this isn't the exact equivalent of the code above as we also create components, add them to the UI and handle the resulting error!
a more fair comparison would be:

try {
    Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
    ...
} catch(IOException err) {
    ...
}

That's effectively one line of code that could even be shorter after which we have the result... No flow, no callback!
Developers often pour hate on the Java checked exceptions feature and I have to agree that they are sometimes painful (f'ing InterruptedException is stupid) but this is a great example of why checked exceptions matter. We MUST handle errors properly and we can't just ignore it until our code reaches production with this lovely "TODO" comment that no one bothered reading.

One Language - Less Code

The listings seem roughly equivalent in size but you will notice the react code ignores the native platform specific code when dealing with the JavaScript code. Our listing is all encompassing, no additional code is needed and no further boilerplate, projects etc.
React Native takes this even further by mixing tags with the JavaScript code effectively mixing declarative code into the regular flow. Yes it shortens the code, but also removes a huge part of the value of declarative programming which is the separation of responsibilities.

Reload == Apply Code Changes

React Native can be debugged by reloading which is there to help when working with the awful Android emulator. Luckily Codename One doesn't need that emulator, you also don't need to restart your app to reload compiled changes... E.g. in NetBeans just use "Apply Code Changes" in the debugger and your changes are instantly mirrored into a running app.

Scripting Languages Are Problematic "On Device"

This isn't quite a "React Native" specific rant, its related to all tools packaging JavaScript in the app bundle. Scripting languages are great for the web, they are like "duct tape". Show me a hacker who doesn't LOVE duct tape!
The temptation to ship an app built with such duct tape is big, but unlike the web where you can just fix that "weird undefined" bug in production by deploying a new update. With apps you need to go thru Apples approval process... This means production bugs that stay while you watch your rating drop.
Yes, unit tests, lint and a lot of other solutions are supposed to catch those things but when you use a modern IDE and it detects potential null inference thanks to the strict language syntax its pretty amazing!

E.g. a great example for JavaScripts over simplification of problems would be in code like this:

function reduce(var a) {
      if(...) {
         a = a - 1;
      } else {
         a = a + 1;
      }
}

If this was Java code we could tell exactly what would happen here... In JavaScript this isn't quite the case!
Lets assume that due to a bug a was somehow a string that is "11" as long as the condition is true (which might be the case in all test cases) this will act like a number. E.g. a will become "10".
But in production if the condition becomes false for some reason a would become "111".
If a represents something of value (e.g. debt, credit etc.) having an app with this bug in the store could be really painful.

Environment

React native uses the native development environments which means it needs a Mac for iOS development. It also means you do part of the work in the Android IDE, part of it in Xcode and the JavaScript work using a text editor.
Its amazing to me that developers are willing to throw away 30 years of IDE evolution for some syntactic candy???
Are we that traumatized by Eclipse?
Todays IDE's are amazing and the fact you can track/debug your entire code via a single IDE is invaluable. The ability we have as a team to instantly see who used what and for what purpose is astounding, I can't fathom how something like this can be used by a team of more than 2 people especially in a distributed workforce.

What I Liked About JavaScript

The one thing I really like about working with JavaScript is the ease of working with JSON, while in the code below I reduced it significantly almost to the same size its still not as elegant.
I'm still not a fan of duck typing or scripting languages but I'd really like to get something like property objects into Codename One and improve the integrated parsing.

Final Word

One of the problems I find with terse programming is that people use it to hide basic concepts so too much happens in an "unspoken" way. This makes terse code as easy to read as a Tweet, unfortunately if you need to express even a moderately complex idea Twitter just doesn't cut it and that's a big problem with some of these API's.

React native has its fans, after all its probably better than PhoneGap which has its own set of limitations. But its still a limited concept standing on the chicken legs of a scripting infrastructure. It has no real advantage when compared to Codename One and has some obvious potential issues.

Java Listing

public class ReactDemo {
    private static final String REQUEST_URL = "https://raw.githubusercontent.com/facebook/react-native/master/docs/MoviesExample.json";
    private Form current;
    private EncodedImage placeholder;

    public void init(Object context) {
        UIManager.initFirstTheme("/theme");
    }
    
    public void start() {
        if(current != null){
            current.show();
            return;
        }
        placeholder = EncodedImage.createFromImage(Image.createImage(53, 81, 0), false);
        Form react = new Form("React Demo", new BorderLayout());
        react.add(BorderLayout.CENTER, new InfiniteContainer() {
            public Component[] fetchComponents(int index, int amount) {
                try {
                    Collection data = (Collection)ConnectionRequest.fetchJSON(REQUEST_URL).get("movies");
                    Component[] response = new Component[data.size()];
                    int offset = 0;
                    for(Object movie : data) {
                        response[offset] = createMovieEntry(Result.fromContent((Map)movie));
                        offset++;
                    }
                    return response;
                } catch(IOException err) {
                    Dialog.show("Error", "Error during connection: " + err, "OK", null);
                }
                return null;
            }
        });
        react.show();
    }
    
    Component createMovieEntry(Result data) {
        Container entry = BorderLayout.center(
                BoxLayout.encloseY(
                        new SpanLabel(data.getAsString("title"), "Line1"), 
                        new Label(data.getAsString("year"), "Line2"))).
                add(BorderLayout.WEST, 
                        URLImage.createToStorage(placeholder, data.getAsString("id"), 
                                    data.getAsString("posters/thumbnail")));
        return entry;
    } 

    public void stop() {
        current = Display.getInstance().getCurrent();
    }
    
    public void destroy() {
    }
}
Share this Post:

Posted by Shai Almog

Shai is the co-founder of Codename One. He's been a professional programmer for over 25 years. During that time he has worked with dozens of companies including Sun Microsystems.
For more follow Shai on Twitter & github.