@front10/landing-page-book
Version:
A react library for landing page
172 lines (156 loc) • 4.96 kB
JavaScript
import React, { createElement } from "react"
import PropTypes from "prop-types"
import loader, { publicLoader } from "./loader"
import emitter from "./emitter"
import { apiRunner } from "./api-runner-browser"
import shallowCompare from "shallow-compare"
const DefaultLayout = ({ children }) => <div>{children()}</div>
// Pass pathname in as prop.
// component will try fetching resources. If they exist,
// will just render, else will render null.
class ComponentRenderer extends React.Component {
constructor(props) {
super()
let location = props.location
// Set the pathname for 404 pages.
if (!loader.getPage(location.pathname)) {
location = Object.assign({}, location, {
pathname: `/404.html`,
})
}
this.state = {
location,
pageResources: loader.getResourcesForPathname(location.pathname),
}
}
componentWillReceiveProps(nextProps) {
// During development, always pass a component's JSON through so graphql
// updates go through.
if (process.env.NODE_ENV !== `production`) {
if (
nextProps &&
nextProps.pageResources &&
nextProps.pageResources.json
) {
this.setState({ pageResources: nextProps.pageResources })
}
}
if (this.state.location.pathname !== nextProps.location.pathname) {
const pageResources = loader.getResourcesForPathname(
nextProps.location.pathname
)
if (!pageResources) {
let location = nextProps.location
// Set the pathname for 404 pages.
if (!loader.getPage(location.pathname)) {
location = Object.assign({}, location, {
pathname: `/404.html`,
})
}
// Page resources won't be set in cases where the browser back button
// or forward button is pushed as we can't wait as normal for resources
// to load before changing the page.
loader.getResourcesForPathname(location.pathname, pageResources => {
this.setState({
location,
pageResources,
})
})
} else {
this.setState({
location: nextProps.location,
pageResources,
})
}
}
}
componentDidMount() {
// Listen to events so when our page gets updated, we can transition.
// This is only useful on delayed transitions as the page will get rendered
// without the necessary page resources and then re-render once those come in.
emitter.on(`onPostLoadPageResources`, e => {
if (
loader.getPage(this.state.location.pathname) &&
e.page.path === loader.getPage(this.state.location.pathname).path
) {
this.setState({ pageResources: e.pageResources })
}
})
}
shouldComponentUpdate(nextProps, nextState) {
// 404
if (!nextState.pageResources) {
return true
}
// Check if the component or json have changed.
if (!this.state.pageResources && nextState.pageResources) {
return true
}
if (
this.state.pageResources.component !== nextState.pageResources.component
) {
return true
}
if (this.state.pageResources.json !== nextState.pageResources.json) {
return true
}
// Check if location has changed on a page using internal routing
// via matchPath configuration.
if (
this.state.location.key !== nextState.location.key &&
nextState.pageResources.page &&
(nextState.pageResources.page.matchPath ||
nextState.pageResources.page.path)
) {
return true
}
return shallowCompare(this, nextProps, nextState)
}
render() {
const pluginResponses = apiRunner(`replaceComponentRenderer`, {
props: { ...this.props, pageResources: this.state.pageResources },
loader: publicLoader,
})
const replacementComponent = pluginResponses[0]
// If page.
if (this.props.page) {
if (this.state.pageResources) {
return (
replacementComponent ||
createElement(this.state.pageResources.component, {
key: this.props.location.pathname,
...this.props,
...this.state.pageResources.json,
})
)
} else {
return null
}
// If layout.
} else if (this.props.layout) {
return (
replacementComponent ||
createElement(
this.state.pageResources && this.state.pageResources.layout
? this.state.pageResources.layout
: DefaultLayout,
{
key:
this.state.pageResources && this.state.pageResources.layout
? this.state.pageResources.layout
: `DefaultLayout`,
...this.props,
}
)
)
} else {
return null
}
}
}
ComponentRenderer.propTypes = {
page: PropTypes.bool,
layout: PropTypes.bool,
location: PropTypes.object,
}
export default ComponentRenderer