diff --git a/README.md b/README.md
index c73411c..8d11f5b 100644
--- a/README.md
+++ b/README.md
@@ -126,6 +126,22 @@ React.BackboneMixin({
});
```
+If in some cases you need to control whatever a change should re-render the view or not:
+```javascript
+var UserViewComponent = React.createBackboneClass({
+ shouldComponentUpdate: function() {
+ return this.getModel().previous('name') != this.getModel().get('name');
+ },
+ render: function() {
+ return (
+
+
{this.getModel().get("name")}
+
+ );
+ }
+});
+```
+
If your Collection or Model class does not inherit directly from Backbone.Model
or Backbone.Collection, you may customize the behavior on a library level by
overriding the React.BackboneMixin.ConsiderAsCollection function.
diff --git a/__tests__/react.backbone-test.js b/__tests__/react.backbone-test.js
index 778d424..49a79d8 100644
--- a/__tests__/react.backbone-test.js
+++ b/__tests__/react.backbone-test.js
@@ -20,23 +20,23 @@ describe('react.backbone', function() {
it('renders model as-is', function() {
var user = new Backbone.Model({name: "Clay"});
- var userViewRef = UserView({model: user});
+ var userViewRef = React.createFactory(UserView)({model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
expect(header.getDOMNode().textContent).toEqual('Clay');
});
-
+
it('renders model after changes to property', function() {
var user = new Backbone.Model({name: "Clay"});
- var userViewRef = UserView({model: user});
+ var userViewRef = React.createFactory(UserView)({model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
user.set("name", "David");
var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
expect(header.getDOMNode().textContent).toEqual('David');
});
-
+
describe("with changeOptions", function() {
var UserView = React.createBackboneClass({
changeOptions: "change:name",
@@ -51,7 +51,7 @@ describe('react.backbone', function() {
it('doesnt render if other property is changed', function() {
var user = new Backbone.Model({name: "Clay", age: "80"});
- var userViewRef = UserView({model: user});
+ var userViewRef = React.createFactory(UserView)({model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
user.set("age", "60");
@@ -61,7 +61,7 @@ describe('react.backbone', function() {
it('does render if valid property is changed', function() {
var user = new Backbone.Model({name: "Clay", age: "80"});
- var userViewRef = UserView({model: user});
+ var userViewRef = React.createFactory(UserView)({model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
user.set("name", "David");
@@ -69,14 +69,57 @@ describe('react.backbone', function() {
expect(header.getDOMNode().textContent).toEqual('David 80');
});
+ });
+
+ describe("with shouldComponentUpdate", function() {
+ var UserView = React.createBackboneClass({
+ shouldComponentUpdate: function(){
+ return this.getModel().previous('name') != this.getModel().get('name');
+ },
+ render: function() {
+ return (
+
+
{this.getModel().get("name")} {this.getModel().get("age")}
+
+ );
+ }
+ });
+
+ it('doesnt auto-update if shouldComponentUpdate is false', function() {
+
+ var user = new Backbone.Model({name: "Mehdi", age: "80"});
+ var userViewRef = React.createFactory(UserView)({model: user});
+ var userView = TestUtils.renderIntoDocument(userViewRef);
+ user.set("age", "100");
+
+ var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
+ expect(header.getDOMNode().textContent).toEqual('Mehdi 80');
+ });
+
+ it('forces update even if shouldComponentUpdate is false', function() {
+ var user = new Backbone.Model({name: "Mehdi", age: "80"});
+ var userViewRef = React.createFactory(UserView)({model: user});
+ var userView = TestUtils.renderIntoDocument(userViewRef);
+ user.set("age", "100");
+
+ var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
+ expect(header.getDOMNode().textContent).toEqual('Mehdi 80');
+
+ userView.forceUpdate()
+ expect(header.getDOMNode().textContent).toEqual('Mehdi 100');
+ });
+
+
+
+
});
});
describe("with :collection key", function() {
var UsersListView = React.createBackboneClass({
render: function() {
- var usersList = this.getCollection().map(function(user) {
- return {user.get("name")};
+ var usersList = this.getCollection().map(function(user, index) {
+ return {user.get("name")};
});
return (
@@ -89,7 +132,7 @@ describe('react.backbone', function() {
it('renders collection as-is', function() {
var usersList = new Backbone.Collection([{name: "Clay"}, {name: "David"}]);
- var usersListViewRef = UsersListView({collection: usersList});
+ var usersListViewRef = React.createFactory(UsersListView)({collection: usersList});
var usersListView = TestUtils.renderIntoDocument(usersListViewRef);
jest.runOnlyPendingTimers();
@@ -102,7 +145,7 @@ describe('react.backbone', function() {
it('renders collection on adding', function() {
var usersList = new Backbone.Collection([{name: "Clay"}, {name: "David"}]);
- var usersListViewRef = UsersListView({collection: usersList});
+ var usersListViewRef = React.createFactory(UsersListView)({collection: usersList});
var usersListView = TestUtils.renderIntoDocument(usersListViewRef);
usersList.add({name: "Jack"});
@@ -112,6 +155,41 @@ describe('react.backbone', function() {
expect(list.getDOMNode().childNodes.length).toEqual(3);
expect(list.getDOMNode().childNodes[2].textContent).toEqual("Jack");
});
+
+ describe("with shouldComponentUpdate", function() {
+ it('doesnt auto-update if shouldComponentUpdate is false', function() {
+
+ var UsersListView = React.createBackboneClass({
+ shouldComponentUpdate: function(){
+ return false;
+ },
+ render: function() {
+ var usersList = this.getCollection().map(function(user, index) {
+ return {user.get("name")};
+ });
+
+ return (
+
+ );
+ }
+ });
+
+ var usersList = new Backbone.Collection([{name: "Mehdi"}, {name: "David"}]);
+ var usersListViewRef = React.createFactory(UsersListView)({collection: usersList});
+ var usersListView = TestUtils.renderIntoDocument(usersListViewRef);
+ usersList.add({name: "Jack"});
+
+ jest.runOnlyPendingTimers();
+
+ var list = TestUtils.findRenderedDOMComponentWithTag(usersListView, 'ul');
+ expect(list.getDOMNode().childNodes.length).toEqual(2);
+ expect(list.getDOMNode().childNodes[2]).toEqual(null);
+
+ });
+
+ });
});
describe("with mixins", function() {
@@ -133,7 +211,7 @@ describe('react.backbone', function() {
it("should render mixins as-is", function() {
var user = new Backbone.Model({name: "Clay"});
var wall = new Backbone.Model({post_count: 5});
- var profileViewRef = ProfileView({user: user, wall: wall});
+ var profileViewRef = React.createFactory(ProfileView)({user: user, wall: wall});
var profileView = TestUtils.renderIntoDocument(profileViewRef);
var header = TestUtils.findRenderedDOMComponentWithTag(profileView, 'h1');
@@ -147,7 +225,7 @@ describe('react.backbone', function() {
it("should re-render if either mixin model is changed", function() {
var user = new Backbone.Model({name: "Clay"});
var wall = new Backbone.Model({post_count: 5});
- var profileViewRef = ProfileView({user: user, wall: wall});
+ var profileViewRef = React.createFactory(ProfileView)({user: user, wall: wall});
var profileView = TestUtils.renderIntoDocument(profileViewRef);
user.set("name", "David");
@@ -177,7 +255,7 @@ describe('react.backbone', function() {
});
it("should use that prop", function() {
var user = new Backbone.Model({name: "Clay"});
- var userViewRef = UserView({user_model: user});
+ var userViewRef = React.createFactory(UserView)({user_model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
@@ -203,7 +281,7 @@ describe('react.backbone', function() {
});
it("should use that event", function() {
var user = new Backbone.Model({name: "Clay", age: 25});
- var userViewRef = UserView({user_model: user});
+ var userViewRef = React.createFactory(UserView)({user_model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
var header;
@@ -235,7 +313,7 @@ describe('react.backbone', function() {
});
it("should use that return value", function() {
var user = new Backbone.Model({name: "Clay"});
- var userViewRef = UserView({user_model: user});
+ var userViewRef = React.createFactory(UserView)({user_model: user});
var userView = TestUtils.renderIntoDocument(userViewRef);
var header = TestUtils.findRenderedDOMComponentWithTag(userView, 'h1');
@@ -268,7 +346,7 @@ describe('react.backbone', function() {
expect(object).toEqual(users);
return true;
};
- var userViewRef = UserView({ collection: users });
+ var userViewRef = React.createFactory(UserView)({collection: users});
TestUtils.renderIntoDocument(userViewRef);
});
it("should pass model to config function for test", function () {
@@ -278,7 +356,7 @@ describe('react.backbone', function() {
expect(object).toEqual(user);
return false;
};
- var userViewRef = UserView({ model: user });
+ var userViewRef = React.createFactory(UserView)({model: user});
TestUtils.renderIntoDocument(userViewRef);
});
@@ -296,7 +374,7 @@ describe('react.backbone', function() {
React.BackboneMixin.ConsiderAsCollection = function (object) {
return false;
};
- var userViewRef = UserView({ model: usersModel });
+ var userViewRef = React.createFactory(UserView)({model: usersModel});
TestUtils.renderIntoDocument(userViewRef);
});
@@ -306,7 +384,7 @@ describe('react.backbone', function() {
React.BackboneMixin.ConsiderAsCollection = function (object) {
return true;
};
- var userViewRef = UserView({ collection: usersCollection });
+ var userViewRef = React.createFactory(UserView)({collection: usersCollection});
TestUtils.renderIntoDocument(userViewRef);
});
@@ -314,5 +392,4 @@ describe('react.backbone', function() {
});
});
-
});
\ No newline at end of file
diff --git a/react.backbone.js b/react.backbone.js
index 7077751..42249ac 100644
--- a/react.backbone.js
+++ b/react.backbone.js
@@ -25,6 +25,13 @@
updateScheduler: _.identity
};
+ var updatable = function (component){
+ if (component.shouldComponentUpdate !== null){
+ return component.shouldComponentUpdate();
+ }
+ return true;
+ }
+
var subscribe = function(component, modelOrCollection, customChangeOptions) {
if (!modelOrCollection) {
return;
@@ -33,7 +40,7 @@
var behavior = React.BackboneMixin.ConsiderAsCollection(modelOrCollection) ? collectionBehavior : modelBehavior;
var triggerUpdate = behavior.updateScheduler(function() {
- if (component.isMounted()) {
+ if (component.isMounted() && updatable(component)) {
(component.onModelChange || component.forceUpdate).call(component);
}
});
@@ -79,7 +86,7 @@
unsubscribe(this, modelOrCollection(this.props));
subscribe(this, modelOrCollection(nextProps), customChangeOptions);
-
+
if (typeof this.componentWillChangeModel === 'function') {
this.componentWillChangeModel();
}
@@ -98,6 +105,7 @@
componentWillUnmount: function() {
// Ensure that we clean up any dangling references when the component is destroyed.
unsubscribe(this, modelOrCollection(this.props));
+
}
};
};