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

Using React in the Real World

DZone's Guide to

Using React in the Real World

React is a really useful tool. Learn all about using React,js to create an awesome small, well-contained project.

· Web Dev Zone
Free Resource

Learn how to build modern digital experience apps with Crafter CMS. Download this eBook now. Brought to you in partnership with Crafter Software

Did you know, you can use React without rewriting your whole app?

A client recently asked me for a shiny graph. They had an API that spits out a lot of data, and they wanted an interactive visualization that would help users make decisions. Deadline was tight, technology restrictions were “As long as it works”, and specs were loose.

“A-ha!”, I thought, “This is a job for React+d3.js!”. Small, well-contained, perfect. The best kind of project for testing new technologies.

React evil plot

React evil plot

But their web app was built in Joomla or WordPress or something. And if I understand correctly, their front-end stack was jQuery. When I asked the engineer about integration, he talked about framework plugins that create views in PHP and load JavaScript files and … stuff.

It sounded a lot like Ruby on Rails gems. Tiny packages that are essentially self-contained apps designed to be integrated into larger projects.

They’d been at it for over a year already. The whole thing built out of these framework plugin component things that put most of the hard work on the server, and only use JavaScript sparingly.

A team of engineers isn’t going to rewrite their whole project into React, just so you can make a small component. I wasn’t going to ask, and they wouldn’t have done it.

I would have to accommodate them.

But how?

The Solution

Well, at the end of the day it’s still just a website. There’s a modern browser, HTML, the DOM model, CSS, all the basics.

But there’s no ES6, no fancy build system, no JavaScript module management. None of the fancy pants future stuff.

And you don’t need any of that stuff. Not on the deploy side. Not on the user side. You only need that stuff when you’re developing. Once you’re done compiling with Webpack, you’re left with run of the mill present-day ES5 code that runs in any mostly modern browser.

I made my client happy by exporting the whole visualization component into a global function that they could call anywhere. It only took a a bit of forethought:

const React = require('react'),
           Chart = require('./hart');

function RenderChart(options) {
    React.render(
        <Counter />,
        document.querySelectorAll(options.selector)[0]
    );
}

window.RenderChart = RenderChart;

Now my client could integrate the React component by just:

  • loading React
  • loading the compiled JS file
  • calling a function with some parameters

Yes, there’s some overhead with loading the whole React library just for one component. But it’s only 38KB after minifying and compressing. jQuery is 29KB, for instance, and nobody bats an eye.

Besides, if you’re using a public CDN, many users won’t even have to download React. Then it’s only 400 bytes to confirm you have an up-to-date version. Not bad at all.

But I global functions don’t feel like the best way to integrate a React component. First of all, they mess with the global namespace, and second of all, they only allow for one-way one-time communication. Once you invoke a component, it’s left to its own devices, and you have no insight.

We can do better.

React Components as jQuery Plugins, Proof-of-Concept

We can package React components as jQuery plugins. Yes, we could make Backbone views, yes, we could make Angular components, yes this is an affront to all things sensible.

And it’s perfect anyway. Name one website or web app built in the last 10 or so years that doesn’t have jQuery? Name one web developer who can’t help themselves with a jQuery plugin?

Exactly.

Here’s a proof of concept I prepared earlier:

You’re looking at a component that tells you how many times you’ve clicked on The Button. That’s it. Nothing fancy.

The rest of the example is a normal old school web app. There’s some jQuery to render the component, and two jQuery buttons to interact with the component once it’s rendered.

That’s right, the white buttons can both read and write the component’s internal state. And they need no React to do it. They look like ordinary jQuery to whomever implements them.

Using the Component/Plugin

The “render my component here” code looks like this:

$(".container .counter").clickCounter();

Nice and easy – select element, call jQuery plugin. Bam.

And the component exposes its internal state via a val() function. Using it looks like this:

$(".btn-10x").click(function () {
    var counter = $(".container .counter")
            .clickCounter()[0];

    counter.val(counter.val()+10);
});

$(".btn-get").click(function () {
    var val = $(".container .counter")
            .clickCounter()[0]
            .val();

    alert("Current counter value is: "+val);
});

We select an element, get its instance of the plugin, and call val() with or without an argument. Depending on what you want.

We’re doing .clickCounter()[0] because I didn’t know how to properly turn the plugin into a per-element singleton. Getting the reference was easy enough, but jQuery selectors can return arrays and that means I have to return an array as well. But then the .val() function gets confused, which is why we have to access a single element of the returned array.

But this is good enough for now. Anyone who knows how to use jQuery can use our React component. And they don’t even need React.

Winning.

Packing React Components Into jQuery Plugins

To turn our React component into a jQuery plugin, we’ll have to take care of two things: making the plugin, compiling our code for the real world.

We’ll take care of the plugin first. I like to put this code in a src/main.jsx file. It serves as an entry point for the component that exports everything the outside world needs. When somebody wants to use my component from React, they can require the component directly.

First we load React, the component, and make a render function:

const React = require('react'),
      Counter = require('./Counter');

function RenderCounter(selector) {
    React.render(
        <Counter />,
        document.querySelectorAll(selector)[0]
    );
}

This will allow us to both export the function, and set it as a global. Like this:

module.exports = RenderCounter;
window.RenderCounter = RenderCounter;

Now anyone who’s got a dependency loader, but isn’t using React, can require our component, and call a function to render it inside a particular element.

Anyone without a dependency loader, can use the global function.

The basics for our jQuery plugin come straight off the jQuery boilerplate project.

if (typeof jQuery !== 'undefined') {
    (function ($) {
        var pluginName = "clickCounter",
            defaults = {
                value: 0
            };

        function Plugin(element, options) {
            this.element = element;
            this.settings = $.extend({}, defaults, options);
            this._defaults = defaults;
            this._name = pluginName;
            this.init();
        }

        $.extend(Plugin.prototype, {
            init: function () {
                this.component = React.render(
                    <Counter value={this.settings.value},
                    this.element
                );
                return this;
            },

            val: function (val) {
                if (!arguments.length) {
                    return this.component.state.counter;
                }else{
                    this.settings.value = val;
                    this.init();
                }
            }
        });
    })(jQuery);
}

Whew, that’s a lot of code.

We’re only making a plugin if jQuery is available, and we’re wrapping its definition in a closure so that we can share the pluginName and defaults variables. Then we’re defining a constructor function called Plugin, which stores some basic properties in the newly constructed object, and calls init.

Inside init, we render our React component. We didn’t have to use document.querySelectorAll, because this.element already refers to the DOM element we want.

And we defined a val function, which we can use to access our component’s internal state. If there’s an argument, we change the value, and re-render the component. We’re banking on React being smart enough to do a normal props refresh rather than instantiating everything from scratch.

If val doesn’t get an argument, it will return the component’s current state.

Okay, we’ve got the plugin function. Now we have to add it to jQuery with a few more lines inside the closure:

        $.fn[pluginName] = function (options) {
            return this.map(function () {
                if (!$.data(this, 'plugin_'+pluginName)) {
                    $.data(this, 'plugin_'+pluginName, new Plugin(this, options));
                }
                return $.data(this, 'plugin_'+pluginName);
            });
        };
    })(jQuery);

This will allow us to call $(...).clickCounter(). And because it saves each instance of the plugin in each element’s $.data collection, we can make sure to treat each instance as a per-element singleton. Every time somebody calls clickCounter on an element, they will get the same object.

Which is neat when you want to mess around with components’ internal values.

Compiling React for the Real World

Great, we’ve got the plugin. All we need now is to make a JavaScript file that people in the real world can use.

It doesn’t take much, just telling Webpack to treat React and jQuery as externals, and to put the compiled code in a ./build/counter.js file. The config I used looks like this:

module.exports = {
    entry: [
        './src/main.jsx'
    ],
    output: {
        filename: 'counter.js',
        path: path.join(__dirname, 'build'),
        publicPath: '/build/'
    },
    module: {
        loaders: [
            {
                test: /\.jsx$/,
                loaders: ['babel'],
                include: path.join(__dirname, 'src')
            }
        ]
    },
    resolve: {
        extensions: ['', '.js', '.jsx']
    },
    externals: {
        "react": "React",
        "react/addons": "React",
        "jQuery": "jQuery"
    },
};

With this, we tell Webpack that the entry point for our module is ./src/main.jsx. That’s where we put the plugin code.

Then we told it where to put compiled code – ./build/counter.js, and that it should use babel-loader for source files. This allows us to use React’s JSX syntax for embedding HTML in JavaScript, and all the fancy new features of ES6 as well.

Using externals, we explained that both React and jQuery are external dependencies. This way Webpack won’t try to bundle them into the compiled file.

It might make sense in some cases to bundle both of those into the same file, but we lose the benefit of serving from CDNs and the users’s browser cache.

Sum Up

There you go, a React component packaged as a jQuery plugin. For when you don’t have the time or the budget to commit to a full rewrite.

You write the component as you always would, then make a thin jQuery wrapper, and make sure people outside the ecosystem can include your file. Bam.

You can find the whole example code on Github, here.

What have I done …

Related

You should follow me on twitter, here.

Crafter is a modern CMS platform for building modern websites and content-rich digital experiences. Download this eBook now. Brought to you in partnership with Crafter Software.

Topics:
web dev ,java ,reactjs

Published at DZone with permission of Swizec Teller, DZone MVB. See the original article here.

Opinions expressed by DZone contributors are their own.

THE DZONE NEWSLETTER

Dev Resources & Solutions Straight to Your Inbox

Thanks for subscribing!

Awesome! Check your inbox to verify your email so you can start receiving the latest in tech news and resources.

X

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

{{ parent.tldr }}

{{ parent.urlSource.name }}