_app.tsx 3.0 KB
Newer Older
L
Lukáš Huvar 已提交
1
import React, { ErrorInfo } from 'react'
A
Alexander Nanberg 已提交
2
import PropTypes from 'prop-types'
J
Joe Haddad 已提交
3 4 5 6 7 8 9
import {
  execOnce,
  loadGetInitialProps,
  AppContextType,
  AppInitialProps,
  AppPropsType,
} from 'next-server/dist/lib/utils'
10
import { Router, makePublicRouterInstance } from '../client/router'
11

12
export { AppInitialProps }
13

14
export type AppContext = AppContextType<Router>
15

16
export type AppProps<P = {}> = AppPropsType<Router, P>
17

L
Lukáš Huvar 已提交
18 19 20 21
/**
 * `App` component is used for initialize of pages. It allows for overwriting and full control of the `page` initialization.
 * This allows for keeping state between navigation, custom error handling, injecting additional data.
 */
J
Joe Haddad 已提交
22 23 24 25
async function appGetInitialProps({
  Component,
  ctx,
}: AppContext): Promise<AppInitialProps> {
J
JJ Kasper 已提交
26 27 28 29
  const pageProps = await loadGetInitialProps(Component, ctx)
  return { pageProps }
}

30 31 32
export default class App<P = {}, CP = {}, S = {}> extends React.Component<
  P & AppProps<CP>,
  S
J
Joe Haddad 已提交
33
> {
A
Alexander Nanberg 已提交
34
  static childContextTypes = {
35
    router: PropTypes.object,
A
Alexander Nanberg 已提交
36
  }
J
JJ Kasper 已提交
37 38
  static origGetInitialProps = appGetInitialProps
  static getInitialProps = appGetInitialProps
39

40
  getChildContext() {
A
Alexander Nanberg 已提交
41
    return {
42
      router: makePublicRouterInstance(this.props.router),
A
Alexander Nanberg 已提交
43 44 45
    }
  }

46
  // Kept here for backwards compatibility.
T
Tim Neutkens 已提交
47 48 49 50
  // When someone ended App they could call `super.componentDidCatch`.
  // @deprecated This method is no longer needed. Errors are caught at the top level
  componentDidCatch(error: Error, _errorInfo: ErrorInfo): void {
    throw error
51 52
  }

53
  render() {
54
    const { router, Component, pageProps } = this.props as AppProps<CP>
55
    const url = createUrl(router)
56 57 58 59 60
    return (
      <Container>
        <Component {...pageProps} url={url} />
      </Container>
    )
61 62 63
  }
}

64 65 66
// @deprecated noop for now until removal
export function Container(p: any) {
  return p.children
67 68 69 70
}

const warnUrl = execOnce(() => {
  if (process.env.NODE_ENV !== 'production') {
L
Lukáš Huvar 已提交
71
    console.error(
J
Joe Haddad 已提交
72
      `Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`
L
Lukáš Huvar 已提交
73
    )
74 75 76
  }
})

77
export function createUrl(router: Router) {
78
  // This is to make sure we don't references the router object at call time
79
  const { pathname, asPath, query } = router
80
  return {
81
    get query() {
82 83 84
      warnUrl()
      return query
    },
85
    get pathname() {
86 87 88
      warnUrl()
      return pathname
    },
89
    get asPath() {
90 91 92 93 94 95 96
      warnUrl()
      return asPath
    },
    back: () => {
      warnUrl()
      router.back()
    },
97
    push: (url: string, as?: string) => {
98 99 100
      warnUrl()
      return router.push(url, as)
    },
101
    pushTo: (href: string, as?: string) => {
102
      warnUrl()
103
      const pushRoute = as ? href : ''
104 105 106 107
      const pushUrl = as || href

      return router.push(pushRoute, pushUrl)
    },
108
    replace: (url: string, as?: string) => {
109 110 111
      warnUrl()
      return router.replace(url, as)
    },
112
    replaceTo: (href: string, as?: string) => {
113
      warnUrl()
114
      const replaceRoute = as ? href : ''
115 116 117
      const replaceUrl = as || href

      return router.replace(replaceRoute, replaceUrl)
118
    },
119 120
  }
}