Skip to content

Instantly share code, notes, and snippets.

@spencer-brown
Last active July 27, 2017 00:03
Show Gist options
  • Save spencer-brown/6d427900b1eaf0f19d2586aec20e72c9 to your computer and use it in GitHub Desktop.
Save spencer-brown/6d427900b1eaf0f19d2586aec20e72c9 to your computer and use it in GitHub Desktop.
import React from 'react';
import PropTypes from 'prop-types';
import CBR from 'connect-backbone-to-react';
/**
* Don't use `BackboneProvider` from `connect-backbone-to-react`; only use `connectBackboneToReact`.
*
* `BackboneProvider` allows you to make Backbone Models and Collections available "globally" for a
* set of components and is bad for a couple of reasons:
* - Components that expect to receive Backbone Models or Collections from their parent component
* via props will not have access
* (https://github.com/mongodb-js/connect-backbone-to-react/blob/master/lib/connect-backbone-to-react.js#L85)
* to Backbone Models or Collections made available using `BackboneProvider`, which gets messy.
* - React makes a strong recommendation against using context (the React feature used by
* `BackboneProvider`) in general
* (https://facebook.github.io/react/docs/context.html#why-not-to-use-context), notably
* mentioning that “If you want your application to be stable, don't use context”.
*/
const { connectBackboneToReact } = CBR;
/**
* This is an example container component. Check out this article for details on why we use
* container components: https://medium.com/@dan_abramov/smart-and-dumb-components-7ca2f9a7c7d0.
*
* We name container components based on whatever they are with no special prefix or suffix. This is
* done so that components that render container components are ignorant of whether or not that
* component is a container or a "combined" component.
*/
class Foo extends React.Component {
/**
* Define class functions in the order that they are used. Here is the list of React lifecycle
* functions in the order that they are called:
* https://facebook.github.io/react/docs/react-component.html#the-component-lifecycle.
*
* There's no need to "_"-prefix private functions as we did with Backbone, since we won't
* instantiate React classes or call their functions outside of JSX.
*/
cleanState() {
return {
clickedAt: null
};
}
constructor(props) {
super(props);
/**
* The React Quick Start docs give a good overview of state and props and when to use each.
*
* We want our components to be as declarative as possible. To achieve this, we should try to
* use Backbone Models and Collections as our data source (referenced through this.props) and
* avoid setting state on React components when possible.
*/
this.state = this.cleanState();
}
/**
* @param {Number} time - Time (in ms) at which the button was clicked.
*/
buttonClicked(time) {
/**
* Uses `this`, so we'll want to use `bind` syntax when passing this function to the template
* component.
*/
this.setState({ clickedAt: time });
}
render() {
return (
<div>
{ /* Use single quotes in JSX :) */ }
<div className='stylish'>{this.props.title}</div>
<FooTemplate
clickedAt={this.state.clickedAt}
{ /* Use the bind syntax described here (https://babeljs.io/docs/plugins/transform-function-bind/) when binding class functions */ }
buttonClicked={::this.buttonClicked}
>
{ /* Check this out for details on composition vs. inheritence: https://facebook.github.io/react/docs/composition-vs-inheritance.html */ }
{this.props.subtitle}
</FooTemplate>
</div>
);
}
}
/**
* PropTypes allow us to define interfaces for components. When passed props do not satisfy these
* specifications, React will (in the local environment only) log a warning. For most components, we
* probably won’t need or want to define interfaces extensively, since it’s not hard for PropTypes
* definitions to reach 50+ lines and we don’t type-check Backbone View arguments today.
*
* You should always set `propTypes` on components you create. ESLint will force you to do this :).
*/
Foo.propTypes = {
title: PropTypes.string.isRequired,
subtitle: PropTypes.string.isRequired
};
/**
* Check out `mapModelsToProps`
* here: https://github.com/mongodb-js/connect-backbone-to-react#connectbackbonetoreact
*/
function mapModelsToProps(models) {
return {
title: models.layout.get('title'),
subtitle: models.layout.get('subtitle')
};
}
export default connectBackboneToReact(mapModelsToProps, {
/**
* Scoping the model/collection events that trigger rerenders allows us to avoid unnecessary
* rerenders. You should specify `events` where possible.
*/
events: {
layout: [
'change:title',
'change:subtitle'
]
}
/**
* connect-backbone-to-react allows you to specify Model/Collection types here, but we should do
* all of that in `propTypes` so that the expectations are centralized.
*/
})(Foo);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment