Skip to content

Commit 0b16681

Browse files
committed
feat: Initial import of all functionality!
1 parent 87a3158 commit 0b16681

10 files changed

+651
-18
lines changed

.babelrc

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
{
2+
"presets": [
3+
"react",
4+
["env", {
5+
"targets": {
6+
"browsers": ["last 2 versions", "safari >= 7"],
7+
"node": 6
8+
}
9+
}]
10+
]
11+
}

.eslintrc

Lines changed: 21 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,23 @@
11
{
2-
"extends": ["mongodb-js/node"]
2+
"extends": ["mongodb-js/node"],
3+
"env": {
4+
"browser": true,
5+
"node": true,
6+
"mocha": true
7+
},
8+
"parserOptions": {
9+
"ecmaVersion": 6,
10+
"sourceType": "module",
11+
"ecmaFeatures": {
12+
"jsx": true
13+
}
14+
},
15+
"rules": {
16+
"comma-dangle": ["error", {
17+
"arrays": "always-multiline",
18+
"objects": "always-multiline",
19+
"imports": "always-multiline",
20+
"exports": "always-multiline",
21+
}]
22+
}
323
}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,4 @@
22
node_modules/
33
npm-debug.log
44
.env
5+
dist/

README.md

Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,68 @@
55
## Example
66

77
```javascript
8+
const userModel = new Backbone.Model({ name: 'Harry', laughs: true });
9+
const userCollection = new Backbone.Collection([userModel]);
10+
11+
class MyComponent extends React.Component {
12+
render() {
13+
return (
14+
<div>
15+
My user laughs: {this.props.doesUserLaugh}
16+
17+
<h4>All Users</h4>
18+
<div>
19+
{this.props.users.map(user => (
20+
<div key={user.name}>{user.name}</div>
21+
))}
22+
</div>
23+
</div>
24+
);
25+
}
26+
}
27+
28+
const WrappedComponent = connectBackboneToReact(
29+
// Map your Backbone Model and Collections to names that will be provided to
30+
// connectBackboneToReact's mapModelsToProps function.
31+
{
32+
user: userModel,
33+
allUsers: userCollection,
34+
},
35+
36+
// Maps Models to properties to give to the React Component. Optional.
37+
// Default behavior is to call `.toJSON()` on every Model and Collection.
38+
function mapModelsToProps(models) {
39+
const { user, allUsers } = models;
40+
41+
// Everything returned from this function will be given as a prop to your Component.
42+
return {
43+
doesUserLaugh: user.get('laughs'),
44+
users: allUsers.toJSON(),
45+
setUserLaughs(newVal) {
46+
user.set('laughs', newVal);
47+
},
48+
};
49+
}
50+
51+
// Options.
52+
{
53+
// Should our event handler function be wrapped in a debounce function
54+
// to prevent many re-renders.
55+
debounce: false, // or `true`, or a number that will be used in the debounce function.
56+
57+
// Define what events you want to listen to on your Backbone Model or Collection
58+
// that will cause your React Component to re-render.
59+
// By default it's ['all'] for every Model and Collection given.
60+
events: {
61+
user: ['change:name', 'change:laughs'], // You can disable listening to events by passing in `false`.
62+
},
63+
}
64+
)(MyComponent)
65+
66+
ReactDOM.render(
67+
<WrappedComponent />,
68+
document.getElementById('app')
69+
);
870
```
971

1072
## License

index.js

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
module.exports = require('./lib');
1+
module.exports = require('./dist');

lib/index.js

Lines changed: 118 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,118 @@
1-
/**
2-
*
3-
* @api public
4-
*/
5-
module.exports = function() {};
1+
const hoistStatics = require('hoist-non-react-statics');
2+
const { Component, createElement } = require('react');
3+
const debounceFn = require('lodash/debounce');
4+
const isArray = require('lodash/isArray');
5+
6+
function getDisplayName(name) {
7+
return `connectBackboneToReact(${name})`;
8+
}
9+
10+
function defaultModelsToProps(models) {
11+
return Object.keys(models).reduce((acc, modelKey) => {
12+
const model = models[modelKey];
13+
acc[modelKey] = model.toJSON();
14+
return acc;
15+
}, {});
16+
}
17+
18+
module.exports = function connectBackboneToReact(
19+
modelsMap = {},
20+
modelsToProps = defaultModelsToProps,
21+
options = {}
22+
) {
23+
if (arguments.length === 2 && typeof modelsToProps !== 'function') {
24+
options = modelsToProps;
25+
modelsToProps = defaultModelsToProps;
26+
}
27+
28+
const {
29+
debounce = false,
30+
events = {},
31+
} = options;
32+
33+
function getEventNames(modelName) {
34+
let eventNames = events[modelName];
35+
36+
// Allow turning off event handlers by setting events to false.
37+
if (eventNames === false) {
38+
return [];
39+
}
40+
41+
if (!isArray(eventNames)) {
42+
eventNames = [];
43+
}
44+
45+
if (eventNames.length === 0) {
46+
eventNames.push('all');
47+
}
48+
49+
return eventNames;
50+
}
51+
52+
return function createWrapper(WrappedComponent) {
53+
function getProps() {
54+
return modelsToProps(modelsMap);
55+
}
56+
57+
const wrappedComponentName = WrappedComponent.displayName
58+
|| WrappedComponent.name
59+
|| 'Component';
60+
61+
const displayName = getDisplayName(wrappedComponentName);
62+
63+
class Connect extends Component {
64+
constructor(state, props) {
65+
super(state, props);
66+
67+
this.state = getProps();
68+
69+
this.createNewProps = this.createNewProps.bind(this);
70+
71+
if (debounce) {
72+
const debounceWait = typeof debounce === 'number' ? debounce : 0;
73+
this.createNewProps = debounceFn(this.createNewProps, debounceWait);
74+
}
75+
76+
this.createEventListeners();
77+
}
78+
79+
createEventListeners() {
80+
Object.keys(modelsMap).forEach(mapKey => {
81+
const model = modelsMap[mapKey];
82+
83+
getEventNames(mapKey).forEach(name => {
84+
model.on(name, this.createNewProps, this);
85+
});
86+
});
87+
}
88+
89+
createNewProps() {
90+
this.setState(getProps());
91+
}
92+
93+
componentWillUnmount() {
94+
if (debounce) {
95+
this.createNewProps.cancel();
96+
}
97+
98+
Object.keys(modelsMap).forEach(mapKey => {
99+
const model = modelsMap[mapKey];
100+
101+
getEventNames(mapKey).forEach(name => {
102+
model.off(name, this.createNewProps, this);
103+
});
104+
});
105+
}
106+
107+
render() {
108+
const wrappedProps = Object.assign({}, this.state, this.props);
109+
return createElement(WrappedComponent, wrappedProps);
110+
}
111+
}
112+
113+
Connect.WrappedComponent = WrappedComponent;
114+
Connect.displayName = displayName;
115+
116+
return hoistStatics(Connect, WrappedComponent);
117+
};
118+
};

package.json

Lines changed: 21 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,14 @@
11
{
22
"name": "connect-backbone-to-react",
33
"description": "Connect Backbone Models and Collections to React.",
4-
"version": "0.0.0",
4+
"version": "0.5.0",
55
"scripts": {
66
"fmt": "mongodb-js-fmt",
77
"check": "mongodb-js-precommit",
88
"test": "mocha",
9-
"ci": "npm run check && npm test"
9+
"ci": "npm run check && npm test",
10+
"compile": "babel lib/ --out-dir dist/",
11+
"prepublish": "npm run compile"
1012
},
1113
"homepage": "http://github.com/mongodb-js/connect-backbone-to-react",
1214
"repository": {
@@ -16,13 +18,27 @@
1618
"bugs": {
1719
"url": "git://github.com/mongodb-js/connect-backbone-to-react.git/issues"
1820
},
19-
"dependencies": {},
21+
"dependencies": {
22+
"hoist-non-react-statics": "^1.2.0",
23+
"react": "^15.4.2"
24+
},
2025
"devDependencies": {
26+
"babel-cli": "^6.22.2",
27+
"babel-preset-env": "^1.1.8",
28+
"babel-preset-react": "^6.22.0",
29+
"babel-register": "^6.22.0",
30+
"backbone": "^1.3.3",
31+
"enzyme": "^2.7.1",
2132
"eslint-config-mongodb-js": "^2.2.0",
22-
"mocha": "^2.3.4",
33+
"jsdom": "^9.11.0",
34+
"lodash": "^4.17.4",
35+
"mocha": "^3.2.0",
2336
"mongodb-js-fmt": "^0.0.3",
2437
"mongodb-js-precommit": "^0.2.8",
25-
"pre-commit": "^1.1.2"
38+
"pre-commit": "^1.1.2",
39+
"react-addons-test-utils": "^15.4.2",
40+
"react-dom": "^15.4.2",
41+
"sinon": "^1.17.7"
2642
},
2743
"license": "Apache-2.0",
2844
"precommit": [

0 commit comments

Comments
 (0)