How to use a React Component in a Backbone View
A big part of migrating over from one framework to another is the in-between state. It’s never as simple as flipping a switch — and you might find yourself, like us, juggling two different systems in the same application.
At work, moving from Backbone to React has presented a few challenges: from managing state and nested routers to sharing components and partial templates. While it’s been easy to build new functionality in React, we wanted to be able to use some of our newer (React) components in older (Backbone) views of our application. Luckily, the react-dom package makes this simpler than you’d think. Before we walk through an example, let’s go through a little refresher on the DOM, React, and the Virtual DOM.
Refresher
The DOM (Document Object Model) is a tree-like structure for documents, like web pages, with nodes that come together to represent the UI of your page. When application state changes — you get a notification, your download is complete, etc — the DOM is manipulated to reflect that change. Re-painting the UI for every change is expensive and slow, especially if you have a lot of components.
React introduced the Virtual DOM, a virtual representation of the real DOM. When application state changes, the Virtual DOM is updated and then compared or diff-ed against the real DOM. The most efficient way to update the real DOM is then identified and the update occurs. This is much faster and less expensive.
ReactDOM
ReactDOM
is a package that allows you to manipulate the DOM — it is the layer between React (your components) and the DOM (where they’re rendered). It exposes an API with several important methods: render
, findDOMNode
, unmountComponentAtNode
, etc…
The render method has three arguments:
- element (what you want to render)
- container (where you want to render it)
- callback (an optional function to run after the render is complete)
Now let’s take a look at an example that uses this render method to render a React component in a Backbone View.
Example
Let’s tackle a quick example. I’ve got a simple React component called UserInfo
that displays a user’s name, photo, and role.
class UserInfo extends React.Component {
render() {
const { img, name, title } = this.props;
return (
<div>
<img width="100" src={img} />
<div className="name">{name}</div>
<div className="title">{title}</div>
</div>
);
}
}
I want to render this in a larger Backbone view that represents a greeting or landing page for a User. As a Backbone view, it’s got a template
property and a render
method. The template has some html representing how many tasks the user has — and I’d like to add my React UserInfo
component above this greeting.
const GreetingView = Backbone.View.extend({
template: _.template(
"<div>
<p class='tasks-notice'>
Hey, you've got <b><%= tasks %></b> tasks to work on!
</p>
</div>"
),
render() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});
I’m going to use ReactDOM to insert the React Component into the DOM. So I’ll start by making sure I’ve installed it and imported it into my file. This’ll depend on your build system but it’ll generally be something like:
npm install -save react-dom
var ReactDOM = require(“react-dom”);
OR
import ReactDOM from “react-dom”;
Next, I’m going to want to make a <div>
with a unique id like user-info
that’ll act as a container for my React component. I’ll add this to my Backbone template above the tasks notice.
template: _.template(
"<div>
<div id="user-info></div>
<p class='tasks-notice'>
Hey, you've got <b><%= tasks %></b> tasks to work on!
</p>
</div>"
),
Now, I can use ReactDOM
to render the UserInfo
component into that container div
and pass in the props I want for name, title, and img. Make sure to import your React component first.
render() {
this.$el.html(this.template(this.model.toJSON()));
ReactDOM.render(
<UserInfo name={name} title={title} img={img} />,
this.$("#user-info").get(0)
);
return this;
}
Finally, we need to unmount the component when the Backbone view’s remove method is called. It’s important to do this even when the container the React component is in (<div id=”user-info”></div>
) is removed because it signals to the React component and any nested components in it that they need to be unmounted. This fires the componentWillUnMount
lifecycle method for each React component, which in turn will remove any subscriptions to a Redux store, and will allow the tree to be garbage collected.
So I’ll add a remove method to my Backbone view.
remove() {
ReactDOM.unmountComponentAtNode(this.el);
Backbone.View.prototype.remove.call(this);
}
And that’s it — we’ve got a React component happily existing in a Backbone view. Check out the codepen below for the complete, working example.
See the Pen React Component in Backbone View by Sheri Soliman (@sherisoli) on CodePen.