React + Backbone Model


Last time we looked at how to use Backbone’s router to conditionally display different React components. Today we’ll look at how to display Backbone collections and models in a React component.

Models make for efficient organisation of interface data. Backbone models, in particular, publish all sorts of events which can be used to dynamically update the rendering of an interface.

You can find the accompanying source-code, for this tutorial, at: https://github.com/formativ/tutorial-react-backbone-model.
Please post any errors you find, or questions you have as comments on this article or as issues on GitHub.

Using Backbone

We’ve already covered how to get Backbone’s dependencies, but it would be good to familiarise ourselves with how models are used, in general:

<!doctype html>
<html lang="en">
<head>
<script src="jquery-2.0.3/build/jquery.js"></script>
<script src="underscore-1.5.2/build/underscore.js"></script>
<script src="backbone-1.1.0/build/backbone.js"></script>
</head>
<body>
<script type="text/javascript">

var Profile = Backbone.Model.extend({
defaults : {
name : null,
gender : null,
picture : null
}
});

var profile = new Profile({
name : "Christopher Pitt",
gender : "male",
picture : "http://placekitten.com/200/200"
});

console.log(
"name : " + profile.get("name") + "\n" +
"gender : " + profile.get("gender") + "\n" +
"picture : " + profile.get("picture")
);

</script>
</body>
</html>

There’s nothing particularly tricky about this example. We define the default property values, of the model, by extending the Backbone.Model object. We then instantiate it by providing a hash of values with which to set properties on the model instance.

Values can be retrieved with the get() method. The reason we use it (instead of trying to access the properties directly) is because they aren’t directly accessible. JavaScript — at least the form that is in widespread use — does not support getters and setters to the extend that Backbone might implement them. Using method calls also allows us to listen for property change events, and adjust our interface accordingly.

Rendering Models

Let us create an interface for this Profile model:

<!doctype html>
<html lang="en">
<head>
<script src="jquery-2.0.3/build/jquery.js"></script>
<script src="underscore-1.5.2/build/underscore.js"></script>
<script src="backbone-1.1.0/build/backbone.js"></script>
<script src="react-0.8.0/build/react.js"></script>
<script src="react-0.8.0/build/JSXTransformer.js"></script>
<style>

body {
padding : 20px;
font-family : helvetica, arial;
}

.picture {
margin : 0 0 10px 0;
}

</style>
</head>
<body>
<script type="text/jsx">

/**
* @jsx React.DOM
*/

var Profile = Backbone.Model.extend({
defaults : {
name : null,
gender : null,
picture : null
}
});

var profile = new Profile({
name : "Christopher Pitt",
gender : "male",
picture : "http://placekitten.com/200/200"
});

var CardComponent = React.createClass({
render : function() {
return (
<div className="card">
<div className="picture">
<img src={this.props.profile.get("picture")} />
</div>
<div className="name">
{this.props.profile.get("name")}
<small>
({this.props.profile.get("gender")})
</small>
</div>
</div>
);
}
});

React.renderComponent(
<CardComponent profile={profile} />,
document.body
);

</script>
</body>
</html>

In this example, we’re taking the newly-created profile model instance and passing it to the CardComponent class. Then, inside the render() method, we use the same getters to populate a few simple components.

Populating Models

Using static data is fine for example code, but what if we want to use real data? Let’s connect to Facebook and return our very own profile data.

Head on over to https://developers.facebook.com and select AppsCreate a New App. Give it a display name, a namespace, a category and click Create App.

Take note of the App ID, as we’ll need that for when we integrate with Facebook in our example application. We also need to add localhost to the allowed domains. To do that, click Add Platform and select Website. Enter http://localhost/ as the Site URL and add localhost to the App Domains field. Click Save and you should be good to go!

Let’s take a look at what the embed code, for the Facebook SDK, looks like:

<div id="fb-root"></div>
<script>
window.fbAsyncInit = function() {
FB.init({
appId : '{your-app-id}',
status : true,
xfbml : true
});
};

(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, 'script', 'facebook-jssdk'));
</script>

Be sure to replace {your-app-id} with the App ID of the Facebook app you just created, otherwise you will get JavaScript security errors.

Since we added localhost to Facebook, we’re going to need a local web-server to run the next bit of code. To spin one up, use:

python -m SimpleHTTPServer

This will start a web-server which you can find if you type http://localhost:8000 in your browser.

Finally, let’s see what our code looks like when we combine Backbone models with React and the Facebook SDK:

<!doctype html>
<html lang="en">
<head>
<script src="jquery-2.0.3/build/jquery.js"></script>
<script src="underscore-1.5.2/build/underscore.js"></script>
<script src="backbone-1.1.0/build/backbone.js"></script>
<script src="react-0.8.0/build/react.js"></script>
<script src="react-0.8.0/build/JSXTransformer.js"></script>
<style>

body {
padding : 20px;
font-family : helvetica, arial;
}

.picture {
margin : 10px 0;
}

</style>
</head>
<body>
<button class="connect">Connect</button>
<div class="target"></div>
<div id="fb-root"></div>
<script type="text/jsx">

/**
* @jsx React.DOM
*/

var Profile = Backbone.Model.extend({
defaults : {
name : null,
gender : null,
picture : null
}
});

var CardComponent = React.createClass({
componentWillMount : function() {
profile.on("change", (function() {
this.forceUpdate();
}.bind(this)));
},
componentWillUnmount : function() {
profile.off("change");
},
render : function() {
return (
<div className="card">
<div className="picture">
<img src={this.props.profile.get("picture")} />
</div>
<div className="name">
{this.props.profile.get("name")}
<small>
({this.props.profile.get("gender")})
</small>
</div>
</div>
);
}
});

var connect = document.querySelector(".connect");
var target = document.querySelector(".target");
var profile = new Profile();

var fetchProfile = function() {
React.renderComponent(
<CardComponent profile={profile} />,
target
);

FB.api("/me", "get", {}, function(result) {
profile.set("name", result.name);
profile.set("gender", result.gender);
});

var params = "?redirect=false&width=200&height=200";

FB.api(
"/me/picture" + params,
"get",
{},
function(result) {
profile.set("picture", result.data.url);
}
);
};

var login = function() {
FB.login(function() {
fetchProfile();
});
};

window.fbAsyncInit = function() {

FB.init({
appId : "579585842125092",
status : true,
xfbml : true
});

connect.addEventListener("click", function() {
login();
});

FB.Event.subscribe(
"auth.authResponseChange",
function(response) {
if (response.status === "connected") {
fetchProfile();
}
}
);

};

(function(d, s, id){
var js, fjs = d.getElementsByTagName(s)[0];
if (d.getElementById(id)) {return;}
js = d.createElement(s); js.id = id;
js.src = "//connect.facebook.net/en_US/all.js";
fjs.parentNode.insertBefore(js, fjs);
}(document, "script", "facebook-jssdk"));

</script>
</body>
</html>

There’s a lot going on in this example:

  1. We load the Facebook JavaScript SDK as recommended in the Quickstart guide. This is done by adding the fb-root element, the window.fbAsyncInit() function and the script loader.
  2. After running the FB.init() method, we add two kinds of events: the click event to the .connect button and the authResponseChange event to the FB object. If the user is not authenticated then they will need to click on the connection button to initiate the login process. If they are authenticated then the authResponseChange event will be triggered and the fetchProfile() function can be run.
  3. The fetchProfile() function renders the CardComponent class to the .target element. You should notice (at this point) the empty card is rendered. We immediately fire off two calls to the Graph API: one to fetch the profile data and another to fetch the profile picture. As both of these return, we set the model properties which triggers the call to the forceUpdate() method.

Conclusion

Success! We’ve managed to integrate Backbone models, React and the Facebook SDK. Models give us a power way to manage data, and in turn, update our React components. They are even better when combined with Backbone collections…

If you enjoyed this tutorial; it would be helpful if you could click the Recommend button and share it with other developers.