Last active
July 27, 2017 00:03
-
-
Save spencer-brown/6d427900b1eaf0f19d2586aec20e72c9 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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