Over a million developers have joined DZone.
{{announcement.body}}
{{announcement.title}}

React.js in the Small

DZone's Guide to

React.js in the Small

React.js is one of the most talked about JavaScript frameworks. Does it live up to the hype?

· Mobile Zone
Free Resource

Get gorgeous, multi-touch charts for your iOS application with just a few lines of code.

I’m a big fan of accessible technologies. I get suspicious whenever I encounter a framework or library that is “not worth it for a small project,” because that is a coded statement that means either “It has a broad scope of features that small project won’t take advantage of,” or “it trades off simplicity or user experience for performance or other reasons.” Accessibility also helps open-source projects move from obscure to ubiquitous; if jQuery had required a command-line tool to compile it when it came out, I can practically guarantee you Wouldnt have ever heard about it.

With this in mind, I’m a fan of Javascript frameworks that can be used for small projects as well as large ones. I’ve done this before for Angular 1 (Angular 2 seems not to be worth it for a small project), but I just had occasion to try React.js in creating a sarcastic complement to my previous post, and I thought I’d share the experience.

Defining the App

My last post was about how many different ways one can write web apps.To that end, I made an app that randomly selects a language, framework, and datastore and assigns an acronym to that combination.

First Steps With React.js

I’ve never used React in Javascript before; my only experience is with Om andReagent, so while I may have a head start on the broad strokes, I’m still starting from zero with regards to using it in Javascript.

Even though I’m using Middleman, and unlike the last time, I elected to write this in plain-old-js5, partly because CoffeeScript seems to be falling out of favor and I need to re-up my neural connections in JS again. I also decided not even to use React’sJSX processing, for better or for worse; all DOM in my app would be hand-programmed in Javascript.

First, I went to the Techempower Benchmarks and started my copy-paste adventure, producing the raw data you can find in this gist.Then, I went and got react and linked it in my index.html. Finally, I started writing a component and mounted it:

var LanguageSelect = React.createClass({
  render: function(){
    var opts = [];
    for(var ii=0; ii<LANGUAGES; ii++){
      opts[ii] = React.createElement(
        "option",
        {value: LANGUAGES[ii]},
        LANGUAGES[ii]
      );
    }

    return React.createElement(
      "select",
      {
          value: LANGUAGES[0],
      },
      opts
    );
  }
});

ReactDOM.render(
  React.createElement(LanguageSelect),
  document.getElementById("app")
);

That was alright for a start. I learned that creating React components involves using React.createClass to define classes, and then render them using React.createElement.That part was straightforward.

Refining Things a Bit

Next, I set out to make a generic Select element. I took my LanguageSelect and decided it would receive its list of options as a property (I initially abused initializeState for this before learning a bit more and deciding that state wasn’t needed).

I also decided that my components would live in an App container, where I would store any and all state the application needed (a lesson from working with the Clojure script react libraries). I also decided that passing state up by using onChange events was the way to go.

I made the container first, and (after some experimentation) updated my mounting code accordingly:

var App = React.createClass({
    getInitialState: function(){
        return {
            datastore: DATASTORES[0],
            language: LANGUAGES[0],
            framework: FRAMEWORKS_BY_LANGUAGE[LANGUAGES[0]]
        }
    },

    setLanguage: function(e){
        this.setState({
            language: e.target.value,
            framework: FRAMEWORKS_BY_LANGUAGE[e.target.value][0]
        })
    },
    setDatastore: function(e){ this.setState({datastore: e.target.value}); },
    setFramework: function(e){ this.setState({framework: e.target.value}); },

    render: function(){
        var frameworkOptions = FRAMEWORKS_BY_LANGUAGE[this.props.language];
        var langSelect = React.createElement(
            Select,
            {
                options: LANGUAGES,
                value: this.state.language,
                onChange: this.setLanguage
            }
        );
        return React.DOM.div({},
            React.createElement(Select, {
                options: DATASTORES,
                value: this.state.datastore,
                onChange: this.setDatastore
            }),
            langSelect,
            React.createElement(Select, {
                value: this.state.framework,
                options: frameworkOptions,
                onChange: this.setFramework
            }),
        );
    }
});

ReactDOM.render(
  React.createElement(App),
  document.getElementById("app")
);

Then, I wrote a general-purpose Select component:

var Select = React.createClass({
  updateValue: function(e){
    this.props.onChange(e);
  },
  render: function(){
    var opts = [];
    for(var ii=0; ii<this.props.options.length; ii++){
      opts[ii] = React.createElement(
        "option",
        {value: this.props.options[ii]},
        this.props.options[ii]
      );
    }
    return React.createElement(
      "select",
      {
          value: this.props.value,
          onChange: this.props.onChange
      },
      opts
    );
  }
});

Much better! That’s a bit more general.

One thing to know about React is that everything that you render in your app is a component, from the text to the form to the whole thing itself. It’s just a tree of components, in the same way that the DOM is a tree of Nodes.So needed a component to display the initials of the stack, and another one to display those links that I had painstakingly collected:

var VOWELS = "aeiouyAEIOUY";

var isVowel = function(s){
    return VOWELS.indexOf(s) > -1;
};

var InitialDisplay = React.createClass({
    render: function(){
        var d = this.props.datastore[0],
            l = this.props.language[0],
            f = this.props.framework[0],
            comps = [this.props.datastore, this.props.language, this.props.framework],
            initials = [d, l, f];
            spans = [];

        if(isVowel(d)){
            comps = [this.props.language, this.props.datastore, this.props.framework];
            initials = [l, d, f];
        }else if(isVowel(f)){
            comps = [this.props.datastore, this.props.framework, this.props.language];
            initials = [d, f, l];
        }

        for(var ii=0; ii<initials.length; ii++){
            spans[ii] = React.createElement("span", {key: ii}, initials[ii]);
        }

        return React.DOM.div({},
            React.DOM.h2({},
                "Try the ",
                React.DOM.span({className: "initials"}, spans),
                " stack"
            ),
            React.DOM.section({className: "featuring"},
                "Featuring: ",
                React.createElement(Links, {components: comps}),
                React.DOM.p({}, "Get on the trolley and start writing web applications like it's " + (new Date().getYear() + 1900).toString() + " already!")
            )
        );
    }
});

var Links = React.createClass({
    render: function(){
        var links = [];
        for(var ii=0; ii<this.props.components.length; ii++){
            links[ii] = React.DOM.li({},
                React.DOM.a({href: URLS[this.props.components[ii]]},
                    this.props.components[ii]));
        }
        return React.DOM.ul({className: "links"}, links);
    }
});

Here, the InitialDisplay class is responsible for displaying the initials for the selected datastore, language, and framework, which are passed in as props. It hs a little bit of logic to attempt to put a vowel in the middle of the acronym if possible, too. It, in turn, renders the Links component,which just displays each framework/language/datastore as an a element with its URL as the href.

Finally, I just had to install some randomization machinery, and update the App to display all that stuff I just wrote:


var pickRandom = function(coll){
    return coll[Math.floor(Math.random() * coll.length)];
};

var generateRandomState = function(){
    language = pickRandom(LANGUAGES);
    return {
        datastore: pickRandom(DATASTORES),
        language: language,
        framework: pickRandom(FRAMEWORKS_BY_LANGUAGE[language])
    }

}


var App = React.createClass({
    getInitialState: function(){
        return generateRandomState();
    },
    setLanguage: function(e){
        this.setState({
            language: e.target.value,
            framework: pickRandom(FRAMEWORKS_BY_LANGUAGE[e.target.value])
        })
    },
    setDatastore: function(e){ this.setState({datastore: e.target.value}); },
    setFramework: function(e){ this.setState({framework: e.target.value}); },
    randomize: function(){
        this.setState(generateRandomState());
    },
    render: function(){
        var frameworkOptions = FRAMEWORKS_BY_LANGUAGE[this.state.language];
        var langSelect = React.createElement(
            Select,
            {
                options: LANGUAGES,
                value: this.state.language,
                onChange: this.setLanguage
            }
        );
        return React.DOM.div({},
            React.createElement(InitialDisplay, {
                datastore: this.state.datastore,
                language: this.state.language,
                framework: this.state.framework
            }),
            React.DOM.hr(),
            React.DOM.div({className: "selects"},
                React.createElement(Select, {
                    options: DATASTORES,
                    value: this.state.datastore,
                    onChange: this.setDatastore
                }),
                langSelect,
                React.createElement(Select, {
                    value: this.state.framework,
                    options: frameworkOptions,
                    onChange: this.setFramework
                }),
                " or, ",
                React.DOM.a({onClick: this.randomize, href: "#"},
                    "generate a new random stack"
                )
            )
        );
    }
});

That’s it! The whole thing. Again, here’s the whole app in gist form

Impressions

First off, writing DOM in javascript is not a thing that scales well, and IMO JSX only helps this a bit. When all your layout markup is scattered around in the render methods of your components classes, you have a bizarre distribution of display code scattered about.(which best practices usually demand should be as close together as possible). In Clojurescript, I use kioo to deal with this, but it’s not an option for React.

That’s just a gut impression, for the purposes of this small example React’s way is fine.Beyond that and the general gnarliness of Javascript (especially JS5), it really wasn’t too bad.If there existed a loop comprehension syntax I think most of the code would have been calls to React.DOM and React.createElement.

Overall, I give React.js a pass to use for a small project like this. I might try out coffee-react for it, though.

Epilogue

Here’s the Select that could have been, had I decided to use coffee-react:

Select = React.createClass
  render: ->
    <select value={@props.value} onChange={@props.onChange}>
      {<option key={v} value={v}>{v}<option> for v in @props.options}
    </select>

Why do I hurt myself like this?!

.Net developers: use Highcharts, the industry's leading interactive charting library, without writing a single line of JavaScript.

Topics:
react ,mobile ,react.js ,web dev

Published at DZone with permission of Adam Bard. See the original article here.

Opinions expressed by DZone contributors are their own.

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}