Skip to content

Instantly share code, notes, and snippets.

@sarink
Last active August 29, 2015 14:19
Show Gist options
  • Save sarink/13fdb1df45e627d783a7 to your computer and use it in GitHub Desktop.
Save sarink/13fdb1df45e627d783a7 to your computer and use it in GitHub Desktop.
backbone-react
// use like:
// var sample1 = new SampleModel();
// React.render(<SampleComponent model={sample1} />);
define(["underscore", "backbone", "react"],
function(_, Backbone, React) {
"use strict";
var SampleComponent = React.createClass({
// Give us those sweet react console warnings if we don't pass the right props
propTypes: {
model: React.PropTypes.instanceOf(Backbone.Model).isRequired
},
modelAttrsInState: ["id", "description", "available", "jobNumber"],
// This function returns sort of a "viewmodel" for this component
computeStateFromModel: function() {
var newState = this.props.model.pick(this.modelAttrsInState);
// example of bringing in attrs from a nested model or collection, in this case the model has a sampleImagesCollection
newState.primaryImageSrc = this.props.model.get("sampleImagesCollection").first().get("src");
return newState;
},
// Sets our new state to our "viewmodel"
// This could be done without this function, but having it here serves as sort of a hook, just in case you need it
setStateFromModel: function() {
this.setState(this.computeStateFromModel());
},
getInitialState: function() {
return this.computeStateFromModel();
},
// Pure-render-ish, only update if we need to
shouldComponentUpdate: function(nextProps, nextState) {
return nextProps.model !== this.props.model || !_.isEqual(this.state, nextState);
},
// The handler that's called when our model changes, basically just updates our state to our new "viewmodel"
_handleModelChange: function() {
this.setStateFromModel();
},
// The handler that's called when our sampleImagesCollection changes, basically just updates our state to our new "viewmodel"
_handleSampleImagesChange: function() {
this.setStateFromModel();
},
// Set up our listeners on our model, and anything else that should update this component (like the nested sampleImagesCollection)
componentWillMount: function() {
this.listenTo(this.props.model, "change", this._handleModelChange);
this.listenTo(this.props.model.get("sampleImagesCollection"), "add remove change", this._handleSampleImagesChange);
},
// Destroys our sample model
destroyModel: function (event) {
this.props.model.destroy();
},
// Handler for when delete is clicked, basically just calls destroyModel
_handleDeleteClick: function (event) {
this.destroyModel();
},
// A custom function to return an object valueLink can use for two-way binding between BB Models/React Components
linkModelState: function(attr, model) {
model = model || this.props.model;
return {
value: this.state[attr],
requestChange: function(newValue) { model.set(attr, newValue); }
};
},
render: function render() {
return (
<div className="sample">
<h3>Sample ID:{this.state.id}</h3>
<b>Thumbnail:</b>
<img className="sample-thumbnail" src={this.state.primaryImageSrc} />
<br/><br/>
<b>Description:</b>
<input type="text" className="sample-description" valueLink={this.linkModelState("description")} />
<br/><br/>
<b>Available:</b>
<input type="checkbox" className="sample-available" checkedLink={this.linkModelState("available")} />
<br/><br/>
<b>Job Number:</b>
<input type="text" className="sample-job-number" valueLink={this.linkModelState("jobNumber")} />
<br/><br/>
<span onClick={this.destroyModel} className="sample-delete">Delete Sample {this.state.id}</span>
<br/>
<hr/>
</div>
);
},
// The component is unmounting, so have it stop listening for any model/other changes
componentWillUnmount: function() {
this.stopListening();
}
});
// Let's give our React Component some Backboney Events
_.extend(SampleComponent.prototype, Backbone.Events);
return SampleComponent;
});
define(["underscore", "backbone", "react", "components/SampleComponent"],
function(_, Backbone, React, SampleComponent) {
"use strict";
var SampleListComponent = React.createClass({
propTypes: {
collection: React.PropTypes.instanceOf(Backbone.Collection).isRequired
},
// This will trigger an update if the length changes, but if there's some other reason you need to update this listviewcomponent,
// (keep in mind the modelviews will update themselves) you might need to call forceUpdate() here
computeStateFromCollection: function() {
var newState = {};
newState.samplesCount = this.props.collection.length;
return newState;
},
setStateFromCollection: function() {
this.setState(this.computeStateFromCollection());
},
_handleCollectionAddOrRemove: function() {
this.setStateFromCollection();
},
componentWillMount: function() {
this.listenTo(this.props.collection, "add remove", this._handleCollectionAddOrRemove);
},
addModel: function(model) {
return <SampleView key={"sample-"+model.cid} model={model} />;
},
addNewModel: function() {
this.props.collection.add({});
},
render: function() {
return (
<div className="sample-list-container">
There are currently {this.state.samplesCount} Samples
<button className="add-new-sample" onClick={this.addNewModel}>Add Sample</button>
<div className="sample-list">
{this.props.collection.map(this.addModel)}
</div>
</div>
);
},
componentWillUnmount: function() {
this.stopListening();
}
});
_.extend(SampleComponent.prototype, Backbone.Events);
return SampleListComponent;
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment