Is Using .bind() To Lock-In Arguments A "Code Smell" In ReactJS

We already know that we don't need to use the .bind() method for things like .forEach() and .map() in a ReactJS render() function. But, I still see .bind() being used to lock-in arguments at render time. This is often done to bind an event handler (like a click-handler) to a particular item within a virtual DOM collection. But, I wonder, is this a "code smell" in ReactJS? Does this approach go against the "React way?"

WARNING: This post is very much about "feelings" and not about "facts."

The "React way" isn't clearly codified in any one particular statement. But, each framework has its opinions on how problems should be solved. For the purposes of this post, I'm going to consider this passage from the "Thinking in React" documentation:

The first thing you'll want to do is to draw boxes around every component (and subcomponent) in the mock and give them all names. If you're working with a designer, they may have already done this, so go talk to them! Their Photoshop layer names may end up being the names of your React components!

But how do you know what should be its own component? Just use the same techniques for deciding if you should create a new function or object. One such technique is the single responsibility principle, that is, a component should ideally only do one thing. If it ends up growing, it should be decomposed into smaller subcomponents.

The big take-away here, for me, is the emphasis on the single responsibility principle for your components and subcomponents. This, combined with the one-way data flow, seems to be a big part of the "React Way."

Keeping that in mind, let's look at an example of how I sometimes see the .bind() method being used. In the following code, we have a list of friends, each with a "click count." If you click on any friend in the list, the click count for the selected friend is incremented. In the following code, take note of how .bind() is used to configure the click-handler:

When the top-level component goes to render the virtual DOM (Document Object Model), it has to generate a list of friends. And, for each friend, it is binding a click-handler:

onClick={ this.handleClick.bind( this, friend.id ) }

In this case, we are using the Function.prototype.bind() method to partially apply the "friend.id" value as an argument. This way, when the event handler is invoked by the ReactJS event delegation system, we receive both the friend ID as well as the synthetic browser event.

So here's the "code smell." First off, this just feels wrong to me - which, I admit, is 100% opinion, not fact. Generally speaking, I try to avoid the .bind() method, which is usually easy to do with ReactJS' pre-bound methods. But, I don't want to go on "feelings" alone; I want to consider this in the light of the "React way."

NOTE: ReactJS doesn't pre-bind ES6 class methods.

Earlier, we talked about the single-responsibility principle as being part of the "React way." To me, using .bind() in this way violates this principle in two ways. First, it feels like we're overloading the concept of the event-handler. To me, the event handler should only be concerned with the event, not with the data. But, when we bind the data to the event handler, it "feels" like we're conflating two responsibilities.

Second, using .bind() in this way gives the top-level component too many responsibilities. Not only does the top-level component provide the business logic and render the collection of friends, it is also concerned with the item-level interactions and how those interactions feed back into the business logic. To me, it makes more sense - and feels more in alignment with the "React way" - to break the friends collection up into a collection of subcomponents, each of which is responsible for piping UI (user interface) events back into the business logic.

Here's what that might look like. In this version, the top-level component is rendering a collection of Friend components, each of which is given a friend instance and a view-model method to consume:

<!doctype html>

<html>

<head>

<meta charset="utf-8" />

<title>

Is Using .bind() To Lock-In Arguments A "Code Smell" In ReactJS

</title>

<link rel="stylesheet" type="text/css" href="./demo.css"></link>

</head>

<body>

<h1>

Is Using .bind() To Lock-In Arguments A "Code Smell" In ReactJS

</h1>

<p>

<strong>Current Approach</strong>: Using a <em>sub-component</em>.

</p>

<div id="content">

<!-- This content will be replaced with the React rendering. -->

</div>

<!-- Load scripts. -->

<script src="../../vendor/reactjs/react-0.13.3.js"></script>

<script src="../../vendor/reactjs/JSXTransformer-0.13.3.js"></script>

<script src="../../vendor/lodash/lodash-3.9.3.js"></script>

<script type="text/jsx">

// I manage the Demo widget.

var Demo = React.createClass({

// I return the initial state (and setup instance properties) for the component.

getInitialState: function() {

return({

friends: [

{

id: 1,

name: "Joanna",

clickCount: 0

},

{

id: 2,

name: "Sarah",

clickCount: 0

},

{

id: 3,

name: "Kim",

clickCount: 0

}

]

});

},

// ---

// PUBLIC METHODS.

// ---

// I increment the click-count for the friend with the given ID.

incrementCount: function( friendID ) {

// Clone the data to make sure we aren't mutating values in-place.

var friends = _.clone( this.state.friends, true );

var friend = _.find(

friends,

{

id: friendID

}

);

friend.clickCount++;

this.setState({

friends: friends

});

},

// I return the virtual DOM based on the current state.

render: function() {

// Map the friends collection onto a virtual DOM collection. In this

// version, rather than using .bind(), we are going to render an instance

Here, the top-level component simply tells the Friend component that an increment() method is available - it doesn't concern itself with how that method actually gets invoked; that's no longer its responsibility, it's the responsibility of the subcomponent.

To me, this approach feels cleaner and it feels much more like the "React way." But, like I said above, this is a matter of opinion. My opinion. And a reflection of how I like to write code. Your mileage may vary.

I am the co-founder and lead engineer at InVision App, Inc — the world's leading prototyping,
collaboration & workflow platform. I also rock out in JavaScript and ColdFusion 24x7 and I dream about
promise resolving asynchronously.