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

Prototype React 16 context #19

Closed
wants to merge 4 commits into from
Closed
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions lib/backbone-provider.js
Original file line number Diff line number Diff line change
@@ -1,25 +1,30 @@
const { Component, Children } = require('react');
const React = require('react');
const {Children, Component} = React;
const PropTypes = require('prop-types');
const ConnectBackboneToReactContext = require('./context.js'); // eslint-disable-line no-unused-vars
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nit, remove file extension.

Also is this eslint still needed?


class BackboneProvider extends Component {
getChildContext() {
return {
models: this.props.models,
constructor(props) {
super(props);

this.state = {
models: props.models,
};
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need to implement getDerivedStateFromProps to copy over new prop models when they are given.

Or I wonder if we could just use this.props as the value given to the provider.

render() {
return Children.only(this.props.children);
return (
<ConnectBackboneToReactContext.Provider value={this.state}>
{Children.only(this.props.children)}
</ConnectBackboneToReactContext.Provider>
);
}
}

BackboneProvider.propTypes = {
models: PropTypes.object,
children: PropTypes.element.isRequired,
};
BackboneProvider.childContextTypes = {
models: PropTypes.object,
};
BackboneProvider.displayName = 'BackboneProvider';

module.exports = BackboneProvider;
73 changes: 32 additions & 41 deletions lib/connect-backbone-to-react.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
const hoistStatics = require('hoist-non-react-statics');
const { Component, createElement } = require('react');
const React = require('react');
const {Component} = React;
const PropTypes = require('prop-types');
const debounceFn = require('lodash.debounce');
const ConnectBackboneToReactContext = require('./context.js'); // eslint-disable-line no-unused-vars
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ditto comment in other file


function getDisplayName(name) {
return `connectBackboneToReact(${name})`;
Expand All @@ -28,7 +30,7 @@ module.exports = function connectBackboneToReact(
const {
debounce = false,
events = {},
modelTypes = {},
modelTypes = {}
} = options;

function getEventNames(modelName) {
Expand Down Expand Up @@ -66,39 +68,26 @@ module.exports = function connectBackboneToReact(
const displayName = getDisplayName(wrappedComponentName);

class ConnectBackboneToReact extends Component {
constructor(props, context) {
super(props, context);
constructor(props) {
super(props);

this.setModels(props, context);

this.state = mapModelsToProps(this.models, this.props);
this.models = {};
this.state = {};

this.createNewProps = this.createNewProps.bind(this);
this.renderChild = this.renderChild.bind(this);

if (debounce) {
const debounceWait = typeof debounce === 'number' ? debounce : 0;
this.createNewProps = debounceFn(this.createNewProps, debounceWait);
}

this.createEventListeners();
}

setModels(props, context) {
const models = Object.assign({}, context.models, props.models);
setModels(models = {}) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Looks like we don't use this method anymore.

validateModelTypes(models);
this.models = models;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't see this being updated ever either. I'm curious if we even need it anymore.

}

createEventListeners() {
Object.keys(this.models).forEach(mapKey => {
const model = this.models[mapKey];
// Do not attempt to create event listeners on an undefined model.
if (!model) return;

this.createEventListener(mapKey, model);
});
}

createEventListener(modelName, model) {
getEventNames(modelName).forEach(name => {
model.on(name, this.createNewProps, this);
Expand All @@ -110,27 +99,34 @@ module.exports = function connectBackboneToReact(
// The only case where this flag is encountered is when this component
// is unmounted within an event handler but the 'all' event is still triggered.
// It is covered in a test case.
if (this.hasBeenUnmounted) {
return;
}
if (this.hasBeenUnmounted) return;

this.setState(mapModelsToProps(this.models, this.props));
}

componentWillReceiveProps(nextProps, nextContext) {
this.setModels(nextProps, nextContext);
this.createNewProps();

renderChild({ models } = {}) {
const newModels = Object.assign({}, models, this.props.models, this.models);
// Bind event listeners for each model that changed.
Object.keys(this.models).forEach(mapKey => {
const model = this.models[mapKey];
if ((this.props.models && this.props.models[mapKey] === this.models[mapKey]) ||
(this.context.models && this.context.models[mapKey] === this.models[mapKey])) {
Object.keys(newModels).forEach(mapKey => {
const model = newModels[mapKey];
if (this.models && this.models[mapKey] === newModels[mapKey]) {
return; // Did not change.
}

this.createEventListener(mapKey, model);
this.models[mapKey] = model;
});
validateModelTypes(this.models);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should be validating models before we create event listeners. If we don't have a test for this already we should add one.


const wrappedProps = Object.assign(
{},
mapModelsToProps(this.models, this.props),
this.props
);

// Don't pass through models prop.
wrappedProps.models = undefined;
return React.createElement(WrappedComponent, wrappedProps);
}

componentWillUnmount() {
Expand All @@ -152,16 +148,11 @@ module.exports = function connectBackboneToReact(
}

render() {
const wrappedProps = Object.assign(
{},
this.state,
this.props
return (
<ConnectBackboneToReactContext.Consumer>
{this.renderChild}
</ConnectBackboneToReactContext.Consumer>
);

// Don't pass through models prop.
wrappedProps.models = undefined;

return createElement(WrappedComponent, wrappedProps);
}
}

Expand Down
2 changes: 2 additions & 0 deletions lib/context.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
const React = require('react');
module.exports = React.createContext('connectBackboneToReact');
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The argument to createContext is the default value. Our default value is not a string. We can safely omit that.

13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,27 +27,28 @@
"dependencies": {
"hoist-non-react-statics": "^1.2.0",
"lodash.debounce": "^4.0.8",
"prop-types": "^15.5.8"
"prop-types": "^15.6.1"
},
"peerDependencies": {
"react": "^0.14.0 || ^15.0.0-0 || ^16.0.0-0"
"react": "^16.0.0-0"
},
"devDependencies": {
"babel-cli": "^6.22.2",
"babel-preset-env": "^1.1.8",
"babel-preset-react": "^6.22.0",
"babel-register": "^6.22.0",
"backbone": "^1.3.3",
"enzyme": "^2.7.1",
"enzyme": "^3.3.0",
"enzyme-adapter-react-16": "^1.1.1",
"eslint-config-mongodb-js": "^2.2.0",
"jsdom": "^9.11.0",
"mocha": "^3.2.0",
"mongodb-js-fmt": "^0.0.3",
"mongodb-js-precommit": "^0.2.8",
"pre-commit": "^1.1.2",
"react": "^15.4.2",
"react-addons-test-utils": "^15.4.2",
"react-dom": "^15.4.2",
"react": "^16.3.2",
"react-dom": "^16.3.2",
"react-test-renderer": "^16.3.2",
"sinon": "^1.17.7",
"standard-version": "^4.0.0"
},
Expand Down
3 changes: 3 additions & 0 deletions test/test-setup.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
require('babel-register');
const Enzyme = require('enzyme');
const Adapter = require('enzyme-adapter-react-16');
Enzyme.configure({ adapter: new Adapter() });
const jsdom = require('jsdom').jsdom;

global.document = jsdom('');
Expand Down