Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implements connectToStores HoC #177

Merged
merged 2 commits into from Apr 19, 2015
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
116 changes: 113 additions & 3 deletions dist/alt-with-addons.js
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
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
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
@@ -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