React components as jQuery plugins

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.

But their web app was built in Joomla or WordPress or something. And 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.

Continue reading ...

Or scroll down for the gist.

A proof of concept

This is a React component, <Counter />. It counts button clicks.

 

Everything else is a normal old school website.

The React component is embedded as a normal jQuery plugin. These buttons access its state via jQuery methods:
jQuery 10x Button jQuery Get Value

Real world interface

The real world can use <Counter /> as a jQuery plugin:


// src/integrate.js
$(".container .counter").clickCounter();

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

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

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

Pretty nice, eh?

You can still use it as a React component too:


// example.jsx
const MyThing = React.createClass({
    render: function () {
        return (
            <div>
                <Counter />
            </div>
        )
    }
});
                    

And if all else fails, you have the function options:


// example.js
var RenderCounter = require('/path/to/counter.js');
RenderCounter(".container .counter");

// OR

window.RenderCounter(".container .counter");
                    

React -> jQuery wrapper


// src/main.jsx
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(
                        ,
                    this.element
                );
                return this;
            },

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

        $.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);
}
                    

Read the whole story ...

Or check the code on Github, here.

~Swizec