ReactJS: State, Props and Reusable Components

So here’s a thing.

As I build more components in ReactJS, I’m starting to uncover some interesting use cases. One such case that came up recently involved reusing components — a component that can be used both on its own and as part of another component. Let me explain.

A new feature on ProjectNom involves connecting your user profile to a Twitter account. To make this a more seamless experience, I decided to build a simple React component that surfaces the current state of your Twitter account.

For example, if you haven’t added your Twitter account yet:

Connect Twitter Account Example

If you want to add your Twitter account, then we have to send you to the Twitter website to authenticate, so we display the following:

Redirect to Twitter Example

And finally, if you’ve already connected your Twitter account:

Twitter Connected Example

As you can see, this component revolves heavily around manipulating a button, so I knew that would be the primary element in the render.

But thinking more generally, the second state with the busy indicator was something that jumped out as a component that I could use elsewhere on the site. So, I decided to build a “BusyButton” component which could be used both as part of this Twitter component but also on its own whenever I needed a button to display a “busy” state.

So first, let’s make a BusyButton suitable for our Twitter component. As described in my earlier post, it’s best practice to store state in the outermost parent component. Child components just use their props. So, using that philosophy, we can build our BusyButton component like so:

module.exports = React.createClass({
	render: function() {
		var buttonStyle = "btn-" + this.props.style;
				
		if (this.props.busy)
		{
			var busyLabel = this.props.busyLabel
				? React.DOM.span({style: {paddingLeft: 10}}, this.props.busyLabel)
				: "";

			return React.DOM.button({className: "btn " + buttonStyle, style: {minWidth: 75}, disabled: "disabled"},
				React.DOM.i({className: "fa fa-circle-o-notch fa-spin"}),
				busyLabel
			);
		}
		else
		{
			var icon = this.props.icon
				? React.DOM.i({className: "fa fa-" + this.props.icon, style: {paddingRight: 10}})
				: "";

			var attributes = {className: "btn " + buttonStyle, type: this.props.type, id: this.props.id, onClick: this.props.onClick};
			
			if (this.props.disabled) {
				attributes.disabled = "disabled";
			}

			return React.DOM.button(attributes,
				icon,
				this.props.label
			);
		}
	}
});

As you can see, this component is driven entirely with props. If this is a child component, then that makes sense. If the parent gets rendered again, it will pass new props to the child, and the child’s behavior will change when it renders.

So far so good.

But now we come to our second acceptance criteria. We want this button to have the same behavior on its own: a button that can be marked as busy, and have the same disabled state and indicator icon.

With our current component, we have to set everything with props. That’s fine for the component’s initial render, but if we want to change the component’s behavior after the fact, we have a problem. Props are supposed to be immutable — an initial state and nothing more. (see: Props in getInitialState Is an Anti-Pattern)

Truth be told, we could ignore this advice and use something like componentWillReceiveProps to make the component behave like we expect. The issue I have with this approach is that it ignores the fact we are fundamentally talking about changes in the component’s state. The “active” and “busy” behaviors are two different states that the button can be in. And the button can freely move between those states as necessary.

This was the conundrum: I needed the component to run from props in order to keep state isolated in one spot, but I also needed the component to maintain state so that it could be easily manipulated when used on its own. I never found a satisfactory answer to this problem, so I welcome any suggestions or best practices for others who may have encountered this scenario.

In the meantime, I’ve done something of a compromise:

module.exports = React.createClass({
	getInitialState: function() {
		return {
			busy: this.props.initialBusy,
			disabled: this.props.initialDisabled
		};
	},

	busy: function() {
		this.setState({busy: true});
	},
	
	activate: function() {
		this.setState({busy: false});
	},
	
	disable: function() {
		this.setState({disabled: true});
	},
	
	enable: function() {
		this.setState({disabled: false});
	},

	render: function() {
		var buttonStyle = "btn-" + this.props.style;
				
		if (this.state.busy)
		{
			var busyLabel = this.props.busyLabel
				? React.DOM.span({style: {paddingLeft: 10}}, this.props.busyLabel)
				: "";

			return React.DOM.button({className: "btn " + buttonStyle, style: {minWidth: 75}, disabled: "disabled"},
				React.DOM.i({className: "fa fa-circle-o-notch fa-spin"}),
				busyLabel
			);
		}
		else
		{
			var icon = this.props.icon
				? React.DOM.i({className: "fa fa-" + this.props.icon, style: {paddingRight: 10}})
				: "";

			var attributes = {className: "btn " + buttonStyle, type: this.props.type, id: this.props.id, onClick: this.props.onClick};
			
			if (this.state.disabled) {
				attributes.disabled = "disabled";
			}

			return React.DOM.button(attributes,
				icon,
				this.props.label
			);
		}
	}
});

I ended up using state, since it conceptually made sense for the component. But rather than manipulate state directly, I’ve exposed some custom functions on the component that allow its user (whether it be a parent component or custom JavaScript) to control whether the button is in an active or busy state.

The disadvantage is that you have to use these functions. Passing down props from the parent no longer works, because this component now has state, and state takes precedence over props. Luckily, this is a basic component with only two primary states (active & busy), so it’s easy to manage.

But it’s clear that React doesn’t have good support for this scenario, and I’m not entirely happy with this solution. For a framework that is built entirely around the concept of reusable components, it isn’t very clear how reusability is supposed to work.

DRY, Browserify

In my last post, I discussed some of the biggest lessons I learned while building my first component in ReactJS. One of those lessons revolved around Browserify, and how it was the best way to leverage component reuse.

As I moved closer to production, I realized there was one particularly nasty side effect to Browserify. Take the following code for example:

var React = require("react");
var ReactDOM = require("react-dom");

// Custom React Component
var RecipeTabs = require("components/RecipeTabs.js");

function initPage()
{
	ReactDOM.render(
	  React.createElement(RecipeTabs),
	  document.getElementById('recipeTabs')
	);
}

Browserify lets us use CommonJS require syntax even though browsers don’t natively support it. One way it does this is by inlining the entire contents of your require’d JavaScript into your script. So, in the example above, our 13-line script will suddenly grow to include all of React, all of ReactDOM and all of the code for RecipeTabs.

If we blindly require React in this manner inside every JavaScript file on our site, then we end up with a bunch of duplicate inlined code. Even worse, we are forcing users to download the same code over and over. The browser only sees unique file names and sizes — it isn’t smart enough to realize that inside those files are large chunks of repeated code. What we really want is one file that contains all of our common code, and reuse that file on every page. That way, the browser only downloads the code once; every other page load uses a cached copy.

Thankfully, Browserify natively provides a solution to this issue.

The trick is to create a single file that contains all JavaScript requires used site-wide. This single bundle can then be included on all of your pages.

Browserify gives us an easy way to do this. For example, using gulp:

gulp.task('browserify-global', function() {
	return browserify()
		.require(['react','react-dom'])
		.transform(envify)
		.bundle()
		.pipe(source('exports.js'))
		.pipe(gulp.dest('./js'));
});

This creates an exports.js file which is a bundle of React and ReactDOM. We can now include this on every page of our site — the browser will download it once and then cache it for every subsequent page.

But we still have a problem. Browserify doesn’t know about exports.js when it processes the rest of the site’s JavaScript. It will go ahead and inline React and ReactDOM as usual wherever it’s require’d.

The second piece to make this work is to tell Browserify to not inline certain require’d libraries:

gulp.task('browserify-custom', function() {
    return glob('./src/**/*.js', function(err, files) {
        if(!err) {
	        var tasks = files.map(function(entry) {
	            return browserify({ entries: [entry] })
	                .external(['react','react-dom'])
	                .bundle()
	                .pipe(source(entry))
	                .pipe(gulp.dest('./js'));
	        });
		}
	});	
});

Now, whenever Browserify encounters a require for ‘react’ or ‘react-dom’, it won’t inline the script. But, as long as we include the exports.js generated in the previous step, the reference will resolve, and it will be able to execute any React or ReactDOM code.

This isn’t limited to third-party JavaScript libraries either. If you have your own code that is referenced across the entire site, then you can include it:

.require(['react','react-dom', {file: './src/pn.js', expose: 'pn'}])

The expose property specifies the name to use in your require statements. In this case, whenever I need to reference code in pn.js, I can simply require(‘pn’).

In the second step, we can now specify pn as an external library:

.external(['react','react-dom','pn'])

ReactJS: What I Learned From My First Component

Today marks an important milestone — I’m blogging about a JavaScript library!

Ever since I took a deep-dive JavaScript boot camp a few months ago, I’ve been eager to start a project that would let me hone my newly-acquired skills. To that end, I’ve been refactoring ProjectNom’s (poorly-coded) JavaScript so that it conforms to best practices.

That’s low-hanging fruit though. What I really wanted to sink my teeth into was something like AngularJS — a modern JavaScript framework that takes the language to another level, opening up entirely new ways of rendering a website (e.g. SPA).

But the problem with Angular is that it’s a full MVC framework, and an opinionated one at that. To truly get the most out of it, I’d have to build ProjectNom from scratch – which I had just finished doing for other reasons, and didn’t want to do again so soon.

At work, it has been proposed that we use ReactJS for any front-end development so that components could be shared and re-used across projects. I didn’t know anything about React except the name, so when I had some free time, I decided to research it.

I started to get really excited. Not only did the philosophy of the framework sound right up my alley, but it was also designed in such a way that it could slot easily into an existing project. After a tutorial or two, I made the decision to take it for a spin on ProjectNom. What follows are my experiences and takeaways as I moved from the perfect world of tutorials to a real world use case.

To be clear, this post is not a tutorial. There are many of those already, including on React’s own website. I include a quick introduction to the theory of React in the next section, but after that I will assume you know the basics of how React works so that we can dig a little deeper.

So what is React?

Let’s imagine for a moment that standard HTML elements — div, ul, input, etc — are like LEGO bricks. They each have a function and purpose; but, on their own, they aren’t very interesting or useful.

React gives us a way to define components. A React component is like a LEGO set – it specifies the pieces and assembly instructions to create an interesting, complex object. It’s easy to define your own component, but there are also a large number of ready-made React components available. Either way, once a component is defined, all you have to do is ask React for it, and it’s ready to use on a web page.

Of course, a static collection of HTML elements is only slightly more interesting than a single HTML element. Most web pages are driven by the data that flows through it, and that’s where React’s power is fully realized. Each React component contains a state and this state defines how the component should look or behave. You can specify an initial state when you first ask React for a component, but that state can be modified at any time in response to new data. You could think of it like a LEGO set that specifies exactly which minifigs should go where, but providing the flexibility to move them around later.

If this sounds as interesting to you as it does to me, then I encourage you to check out a tutorial or two. Feel free to come back here once you understand the basics.

Continue reading