diff --git a/.jshintrc b/.jshintrc index 8249e5be..100fcc36 100644 --- a/.jshintrc +++ b/.jshintrc @@ -2,8 +2,10 @@ "predef": [ "define", "module", - "Firebase" + "Firebase", + "Immutable" ], + "node": true, "bitwise": true, "curly": true, "eqeqeq": true, diff --git a/bower.json b/bower.json index e7940415..1471e41f 100644 --- a/bower.json +++ b/bower.json @@ -33,7 +33,8 @@ ], "dependencies": { "react": "0.12.x", - "firebase": "2.0.x" + "firebase": "2.0.x", + "immutable": "3.4.x" }, "devDependencies": { "jasmine": "~2.0.0" diff --git a/build/header b/build/header index 6a63bef9..b3d3222b 100644 --- a/build/header +++ b/build/header @@ -10,17 +10,19 @@ ;(function (root, factory) { "use strict"; + var Immutable = root.Immutable; if (typeof define === "function" && define.amd) { // AMD - define([], function() { - return (root.ReactFireMixin = factory()); + define(["../bower_components/immutable/dist/immutable.min.js"], function(Immutable) { + return (root.ReactFireMixin = factory(Immutable)); }); } else if (typeof exports === "object") { // CommonJS - module.exports = factory(); + Immutable = require("immutable"); + module.exports = factory(Immutable); } else { // Global variables - root.ReactFireMixin = factory(); + root.ReactFireMixin = factory(Immutable); } -}(this, function() { +}(this, function(Immutable) { "use strict"; diff --git a/gulpfile.js b/gulpfile.js index 0c66962d..788d7643 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -44,6 +44,7 @@ var paths = { "bower_components/firebase/firebase.js", "tests/phantomjs-es5-shim.js", "bower_components/react/react-with-addons.js", + "bower_components/immutable/dist/immutable.js", "src/*.js", "tests/specs/*.spec.js" ] diff --git a/package.json b/package.json index 778f01f5..feafaaad 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ ], "dependencies": { "firebase": "2.0.x", + "immutable": "3.4.x", "react": "0.12.x" }, "devDependencies": { diff --git a/src/reactfire.js b/src/reactfire.js index 9bb1b9c5..d520452a 100644 --- a/src/reactfire.js +++ b/src/reactfire.js @@ -31,6 +31,21 @@ var ReactFireMixin = { this._bind(firebaseRef, bindVar, cancelCallback, false); }, + /* Checks all bound vars to see if they have changed. The check is cheap + * because we are using immutable data structures. Returns true if any of + * the bound vars are updated in `nextState`, otherwise false. + * + * This is intended to be used in a more comprehensive `shouldComponentUpdate`. + */ + boundVarsHaveUpdated: function(nextState) { + for (var varName in nextState) { + if (this.firebaseRefs[varName] && !Immutable.is(this.state && this.state[varName], nextState[varName])) { + return true; + } + } + return false; + }, + /* Creates a binding between Firebase and the inputted bind variable as either an array or object */ _bind: function(firebaseRef, bindVar, cancelCallback, bindAsArray) { this._validateBindVar(bindVar); @@ -55,10 +70,10 @@ var ReactFireMixin = { this.firebaseListeners[bindVar] = firebaseRef.on("value", function(dataSnapshot) { var newState = {}; if (bindAsArray) { - newState[bindVar] = this._toArray(dataSnapshot.val()); + newState[bindVar] = this._toList(dataSnapshot); } else { - newState[bindVar] = dataSnapshot.val(); + newState[bindVar] = this._toOrderedMap(dataSnapshot); } this.setState(newState); }.bind(this), cancelCallback); @@ -109,25 +124,40 @@ var ReactFireMixin = { } }, - - /* Returns true if the inputted object is a JavaScript array */ - _isArray: function(obj) { - return (Object.prototype.toString.call(obj) === "[object Array]"); + _toOrderedMap: function(snapshot) { + var out = Immutable.OrderedMap(); + if (snapshot) { + if (Immutable.OrderedMap.isOrderedMap(snapshot)) { + out = snapshot; + } + else if (typeof(snapshot) === "object") { + out = out.withMutations(function(map) { + snapshot.forEach(function(child) { + var immutableChild = Immutable.fromJS(child.val()); + immutableChild.snapshot = child; + map.set(child.key(), immutableChild); + }); + }); + } + } + return out; }, - /* Converts a Firebase object to a JavaScript array */ - _toArray: function(obj) { - var out = []; - if (obj) { - if (this._isArray(obj)) { - out = obj; + /* Converts a Firebase object to an Immutable List */ + _toList: function(snapshot) { + var out = Immutable.List(); + if (snapshot) { + if (Immutable.List.isList(snapshot)) { + out = snapshot; } - else if (typeof(obj) === "object") { - for (var key in obj) { - if (obj.hasOwnProperty(key)) { - out.push(obj[key]); - } - } + else if (typeof(snapshot) === "object") { + out = out.withMutations(function(list) { + snapshot.forEach(function(child) { + var immutableChild = Immutable.fromJS(child.val()); + immutableChild.snapshot = child; + list.push(immutableChild); + }); + }); } } return out; diff --git a/tests/index.html b/tests/index.html index c51064ec..7b713909 100644 --- a/tests/index.html +++ b/tests/index.html @@ -15,6 +15,9 @@ + + + @@ -24,5 +27,6 @@
+