Skip to content

Instantly share code, notes, and snippets.

@goldhand
Last active September 8, 2016 18:22
Show Gist options
  • Save goldhand/88fe8fca823fceb7130d7362e327bd51 to your computer and use it in GitHub Desktop.
Save goldhand/88fe8fca823fceb7130d7362e327bd51 to your computer and use it in GitHub Desktop.
React content delivery model
import React, {Component, PropTypes} from 'react';
import {connect} from 'react-redux';
import {bindActionCreators} from 'redux';
import {withRouter} from 'react-router';
import * as actions from 'actions/contentActions';
import {getContent} from 'reducers/content';
/**
* connectPage is a high order component that requests page data, if not already available
* depending on the route url
*
* @param {Class} ComponentClass - the component to be decorated
* @returns {Class} - the decorated component
*/
const connectContent = (ComponentClass) => {
@withRouter
@connect(
(state, ownProps) => {
const slug = ownProps.params.slug || 'home';
return {
content: getContent(state.content, slug),
slug,
};
},
dispatch => (bindActionCreators(actions, dispatch)),
)
class ConnectContent extends Component {
static propTypes = {
content: PropTypes.object.isRequired,
fetchPage: PropTypes.func.isRequired,
slug: PropTypes.string.isRequired,
}
componentWillMount() {
const {fetchPage, slug} = this.props;
fetchPage(slug);
}
componentWillReceiveProps(newProps) {
const {fetchPage, slug} = this.props;
if (slug !== newProps.slug) {
fetchPage(newProps.slug);
}
}
render = () => <ComponentClass {...this.props} />
}
return ConnectContent;
};
export default connectContent;
import React, {Component, PropTypes} from 'react';
import connectContent from 'components/ConnectContent';
// import container components
// import ExampleApp from 'components/ExampleApp';
// map imports to strings so they can be selected with data
const sectionTypes = {
// example: ExampleApp,
};
@connectContent
export default class PageApp extends Component {
static propTypes = {
content: PropTypes.object.isRequired,
}
/**
* Dynamically build sections using fetched content data
* Section component options are imported manually and mapped.
* The section.component is a string that matches the desired component type
*
* @returns {ReactComponent[]} - array of rendered react components
*/
buildSections() {
const {content: {sections}} = this.props;
// TODO: show loading or fetch from cache
if (!sections) return null;
return Object.keys(sections).map(id => {
return React.createElement(
sectionTypes[sections[id].type],
{
...sections[id].content,
key: `section-${id}`,
},
);
});
}
render() {
return (
<div>
{this.buildSections()}
</div>
);
}
}
import AppShell from 'components/AppShell'; // common accross all views
import Page from 'components/PageApp';
export default {
getChildRoutes(location, callback) {
callback(null, [
{
path: '/',
component: AppShell,
indexRoute: {component: Page},
getChildRoutes(loc, cb) {
cb(null, [
{
path: '/(:slug)',
component: Page,
},
]);
},
},
])
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment