Skip to content

Commit

Permalink
Merge pull request #177 from goatslacker/115
Browse files Browse the repository at this point in the history
Implements connectToStores HoC
  • Loading branch information
goatslacker committed Apr 19, 2015
2 parents 20c8382 + bd59252 commit aedabf4
Show file tree
Hide file tree
Showing 7 changed files with 380 additions and 24 deletions.
116 changes: 113 additions & 3 deletions dist/alt-with-addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -1165,6 +1165,8 @@ var DispatcherRecorder = _interopRequire(require("../utils/DispatcherRecorder"))

var atomicTransactions = _interopRequire(require("../utils/atomicTransactions"));

var connectToStores = _interopRequire(require("../utils/connectToStores"));

var chromeDebug = _interopRequire(require("../utils/chromeDebug"));

var makeFinalStore = _interopRequire(require("../utils/makeFinalStore"));
Expand All @@ -1180,12 +1182,13 @@ Alt.addons = {
DispatcherRecorder: DispatcherRecorder,
atomicTransactions: atomicTransactions,
chromeDebug: chromeDebug,
connectToStores: connectToStores,
makeFinalStore: makeFinalStore,
withAltContext: withAltContext };

module.exports = Alt;

},{"../../AltContainer":1,"../utils/ActionListeners":23,"../utils/AltManager":24,"../utils/DispatcherRecorder":25,"../utils/atomicTransactions":26,"../utils/chromeDebug":27,"../utils/makeFinalStore":28,"../utils/withAltContext":29,"./":14}],14:[function(require,module,exports){
},{"../../AltContainer":1,"../utils/ActionListeners":23,"../utils/AltManager":24,"../utils/DispatcherRecorder":25,"../utils/atomicTransactions":26,"../utils/chromeDebug":27,"../utils/connectToStores":28,"../utils/makeFinalStore":29,"../utils/withAltContext":30,"./":14}],14:[function(require,module,exports){
"use strict";

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };
Expand Down Expand Up @@ -2335,7 +2338,7 @@ function atomicTransactions(alt) {
};
}

},{"./makeFinalStore":28}],27:[function(require,module,exports){
},{"./makeFinalStore":29}],27:[function(require,module,exports){
/*global window*/
"use strict";

Expand All @@ -2346,6 +2349,113 @@ function chromeDebug(alt) {
}

},{}],28:[function(require,module,exports){
(function (global){
"use strict";

var _interopRequire = function (obj) { return obj && obj.__esModule ? obj["default"] : obj; };

/**
* 'Higher Order Component' that controls the props of a wrapped
* component via stores.
*
* Expects the Component to have two static methods:
* - getStores(): Should return an array of stores.
* - getPropsFromStores(props): Should return the props from the stores.
*
* Example using old React.createClass() style:
*
* const MyComponent = React.createClass({
* statics: {
* getStores() {
* return [myStore]
* },
* getPropsFromStores(props) {
* return myStore.getState()
* }
* },
* render() {
* // Use this.props like normal ...
* }
* })
* MyComponent = connectToStores(MyComponent)
*
*
* Example using ES6 Class:
*
* class MyComponent extends React.Component {
* static getStores() {
* return [myStore]
* }
* static getPropsFromStores(props) {
* return myStore.getState()
* }
* render() {
* // Use this.props like normal ...
* }
* }
* MyComponent = connectToStores(MyComponent)
*
* A great explanation of the merits of higher order components can be found at
* http://bit.ly/1abPkrP
*/

var React = _interopRequire((typeof window !== "undefined" ? window.React : typeof global !== "undefined" ? global.React : null));

var assign = _interopRequire(require("object-assign"));

function connectToStores(Component) {

// Check for required static methods.
if (typeof Component.getStores !== "function") {
throw new Error("connectToStores() expects the wrapped component to have a static getStores() method");
}
if (typeof Component.getPropsFromStores !== "function") {
throw new Error("connectToStores() expects the wrapped component to have a static getPropsFromStores() method");
}

// Cache stores.
var stores = Component.getStores();

// Wrapper Component.
var StoreConnection = React.createClass({
displayName: "StoreConnection",

getInitialState: function getInitialState() {
return Component.getPropsFromStores(this.props);
},

componentDidMount: function componentDidMount() {
var _this = this;

stores.forEach(function (store) {
store.listen(_this.onChange);
});
},

componentWillUnmount: function componentWillUnmount() {
var _this = this;

stores.forEach(function (store) {
store.unlisten(_this.onChange);
});
},

onChange: function onChange() {
this.setState(Component.getPropsFromStores(this.props));
},

render: function render() {
return React.createElement(Component, assign({}, this.props, this.state));
}
});

return StoreConnection;
}

module.exports = connectToStores;

}).call(this,typeof global !== "undefined" ? global : typeof self !== "undefined" ? self : typeof window !== "undefined" ? window : {})
},{"object-assign":10}],29:[function(require,module,exports){
"use strict";

module.exports = makeFinalStore;
Expand Down Expand Up @@ -2391,7 +2501,7 @@ function makeFinalStore(alt) {
return alt.createUnsavedStore(FinalStore);
}

},{}],29:[function(require,module,exports){
},{}],30:[function(require,module,exports){
(function (global){
"use strict";

Expand Down
2 changes: 1 addition & 1 deletion guides/getting-started/view.md
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ componentWillUnmount() {
},
```

A few [mixins](https://github.com/goatslacker/alt/tree/master/mixins) are available to make this boilerplate go away.
A few [mixins](https://github.com/goatslacker/alt/tree/master/mixins) or a ["higher-order-component"](https://github.com/goatslacker/alt/tree/master/utils/connectToStores.js) are available to make this boilerplate go away.

---

Expand Down
2 changes: 2 additions & 0 deletions src/alt/addons.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ import AltManager from '../utils/AltManager'
import DispatcherRecorder from '../utils/DispatcherRecorder'

import atomicTransactions from '../utils/atomicTransactions'
import connectToStores from '../utils/connectToStores'
import chromeDebug from '../utils/chromeDebug'
import makeFinalStore from '../utils/makeFinalStore'
import withAltContext from '../utils/withAltContext'
Expand All @@ -18,6 +19,7 @@ Alt.addons = {
DispatcherRecorder,
atomicTransactions,
chromeDebug,
connectToStores,
makeFinalStore,
withAltContext,
}
Expand Down
95 changes: 95 additions & 0 deletions src/utils/connectToStores.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
/**
* 'Higher Order Component' that controls the props of a wrapped
* component via stores.
*
* Expects the Component to have two static methods:
* - getStores(): Should return an array of stores.
* - getPropsFromStores(props): Should return the props from the stores.
*
* Example using old React.createClass() style:
*
* const MyComponent = React.createClass({
* statics: {
* getStores() {
* return [myStore]
* },
* getPropsFromStores(props) {
* return myStore.getState()
* }
* },
* render() {
* // Use this.props like normal ...
* }
* })
* MyComponent = connectToStores(MyComponent)
*
*
* Example using ES6 Class:
*
* class MyComponent extends React.Component {
* static getStores() {
* return [myStore]
* }
* static getPropsFromStores(props) {
* return myStore.getState()
* }
* render() {
* // Use this.props like normal ...
* }
* }
* MyComponent = connectToStores(MyComponent)
*
* A great explanation of the merits of higher order components can be found at
* http://bit.ly/1abPkrP
*/

import React from 'react'
import assign from 'object-assign'

function connectToStores(Component) {

// Check for required static methods.
if (typeof Component.getStores !== 'function') {
throw new Error('connectToStores() expects the wrapped component to have a static getStores() method')
}
if (typeof Component.getPropsFromStores !== 'function') {
throw new Error('connectToStores() expects the wrapped component to have a static getPropsFromStores() method')
}

// Cache stores.
const stores = Component.getStores()

// Wrapper Component.
const StoreConnection = React.createClass({
getInitialState() {
return Component.getPropsFromStores(this.props)
},

componentDidMount() {
stores.forEach((store) => {
store.listen(this.onChange)
})
},

componentWillUnmount() {
stores.forEach((store) => {
store.unlisten(this.onChange)
})
},

onChange() {
this.setState(Component.getPropsFromStores(this.props))
},

render() {
return React.createElement(
Component,
assign({}, this.props, this.state)
)
}
})

return StoreConnection
}

export default connectToStores
Loading

0 comments on commit aedabf4

Please sign in to comment.