mirror of
https://github.com/terribleplan/next.js.git
synced 2024-01-19 02:48:18 +00:00
d19cc975f4
* Updating React to v16.0.0 * Updating error handling from ReactReconciler to componentDidCatch * Using hydrate() instead of render() on client side. * React 16 is not making `charSet` lowercase but that is in spec.
128 lines
3.4 KiB
JavaScript
128 lines
3.4 KiB
JavaScript
import React, { Component } from 'react'
|
|
import PropTypes from 'prop-types'
|
|
import shallowEquals from './shallow-equals'
|
|
import { warn } from './utils'
|
|
import { makePublicRouterInstance } from './router'
|
|
|
|
export default class App extends Component {
|
|
state = {
|
|
hasError: null
|
|
}
|
|
|
|
static childContextTypes = {
|
|
headManager: PropTypes.object,
|
|
router: PropTypes.object
|
|
}
|
|
|
|
getChildContext () {
|
|
const { headManager } = this.props
|
|
return {
|
|
headManager,
|
|
router: makePublicRouterInstance(this.props.router)
|
|
}
|
|
}
|
|
|
|
componentDidCatch (error, info) {
|
|
error.stack = `${error.stack}\n\n${info.componentStack}`
|
|
window.next.renderError(error)
|
|
this.setState({ hasError: true })
|
|
}
|
|
|
|
render () {
|
|
if (this.state.hasError) return null
|
|
|
|
const { Component, props, hash, router } = this.props
|
|
const url = createUrl(router)
|
|
// If there no component exported we can't proceed.
|
|
// We'll tackle that here.
|
|
if (typeof Component !== 'function') {
|
|
throw new Error(`The default export is not a React Component in page: "${url.pathname}"`)
|
|
}
|
|
const containerProps = { Component, props, hash, router, url }
|
|
|
|
return <div>
|
|
<Container {...containerProps} />
|
|
</div>
|
|
}
|
|
}
|
|
|
|
class Container extends Component {
|
|
componentDidMount () {
|
|
this.scrollToHash()
|
|
}
|
|
|
|
componentDidUpdate () {
|
|
this.scrollToHash()
|
|
}
|
|
|
|
scrollToHash () {
|
|
const { hash } = this.props
|
|
if (!hash) return
|
|
|
|
const el = document.getElementById(hash)
|
|
if (!el) return
|
|
|
|
// If we call scrollIntoView() in here without a setTimeout
|
|
// it won't scroll properly.
|
|
setTimeout(() => el.scrollIntoView(), 0)
|
|
}
|
|
|
|
shouldComponentUpdate (nextProps) {
|
|
// need this check not to rerender component which has already thrown an error
|
|
return !shallowEquals(this.props, nextProps)
|
|
}
|
|
|
|
render () {
|
|
const { Component, props, url } = this.props
|
|
|
|
if (process.env.NODE_ENV === 'production') {
|
|
return (<Component {...props} url={url} />)
|
|
} else {
|
|
const ErrorDebug = require('./error-debug').default
|
|
const { AppContainer } = require('react-hot-loader')
|
|
|
|
// includes AppContainer which bypasses shouldComponentUpdate method
|
|
// https://github.com/gaearon/react-hot-loader/issues/442
|
|
return (
|
|
<AppContainer errorReporter={ErrorDebug}>
|
|
<Component {...props} url={url} />
|
|
</AppContainer>
|
|
)
|
|
}
|
|
}
|
|
}
|
|
|
|
function createUrl (router) {
|
|
return {
|
|
query: router.query,
|
|
pathname: router.pathname,
|
|
asPath: router.asPath,
|
|
back: () => {
|
|
warn(`Warning: 'url.back()' is deprecated. Use "window.history.back()"`)
|
|
router.back()
|
|
},
|
|
push: (url, as) => {
|
|
warn(`Warning: 'url.push()' is deprecated. Use "next/router" APIs.`)
|
|
return router.push(url, as)
|
|
},
|
|
pushTo: (href, as) => {
|
|
warn(`Warning: 'url.pushTo()' is deprecated. Use "next/router" APIs.`)
|
|
const pushRoute = as ? href : null
|
|
const pushUrl = as || href
|
|
|
|
return router.push(pushRoute, pushUrl)
|
|
},
|
|
replace: (url, as) => {
|
|
warn(`Warning: 'url.replace()' is deprecated. Use "next/router" APIs.`)
|
|
return router.replace(url, as)
|
|
},
|
|
replaceTo: (href, as) => {
|
|
warn(`Warning: 'url.replaceTo()' is deprecated. Use "next/router" APIs.`)
|
|
const replaceRoute = as ? href : null
|
|
const replaceUrl = as || href
|
|
|
|
return router.replace(replaceRoute, replaceUrl)
|
|
}
|
|
}
|
|
}
|