未验证 提交 bf294ee4 编写于 作者: L Lukáš Huvar 提交者: GitHub

Types description (#7217)

* App, Document, Page, Error types description

* Router description

* Added head changed router

* Additions

* Fix examples

* Push and replace details

* Update packages/next-server/lib/head.tsx
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next-server/lib/utils.ts
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next/types/index.d.ts
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next/pages/_document.tsx
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next/pages/_document.tsx
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next/pages/_app.tsx
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>

* Update packages/next/pages/_document.tsx
Co-Authored-By: NLuis Fernando Alvarez D. <luis@zeit.co>
上级 ed0e1750
......@@ -9,7 +9,7 @@ export default class MyDocument extends Document {
try {
ctx.renderPage = () =>
originalRenderPage({
enhanceApp: App => props => sheet.collectStyles(<App {...props} />)
enhanceApp: (App) => (props) => sheet.collectStyles(<App {...props} />),
});
const initialProps = await Document.getInitialProps(ctx);
......@@ -20,7 +20,7 @@ export default class MyDocument extends Document {
{initialProps.styles}
{sheet.getStyleElement()}
</>
)
),
};
} finally {
sheet.seal();
......
......@@ -131,6 +131,10 @@ function reduceComponents(headElements: Array<React.ReactElement<any>>, props: W
const Effect = withSideEffect();
/**
* This component injects elements to `<head>` of your page.
* To avoid duplicated `tags` in `<head>` you can use the `key` property, which will make sure every tag is only rendered once.
*/
function Head({ children }: { children: React.ReactNode }) {
return (
<AmpModeContext.Consumer>
......
......@@ -34,6 +34,9 @@ export default class Router implements BaseRouter {
pathname: string
query: ParsedUrlQuery
asPath: string
/**
* Map of all components loaded in `Router`
*/
components: {[pathname: string]: RouteInfo}
subscriptions: Set<Subscription>
componentLoadCancel: (() => void) | null
......@@ -151,14 +154,29 @@ export default class Router implements BaseRouter {
window.location.reload()
}
/**
* Go back in history
*/
back() {
window.history.back()
}
/**
* Performs a `pushState` with arguments
* @param url of the route
* @param as masks `url` for the browser
* @param options object you can define `shallow` and other options
*/
push(url: string, as: string = url, options = {}) {
return this.change('pushState', url, as, options)
}
/**
* Performs a `replaceState` with arguments
* @param url of the route
* @param as masks `url` for the browser
* @param options object you can define `shallow` and other options
*/
replace(url: string, as: string = url, options = {}) {
return this.change('replaceState', url, as, options)
}
......@@ -339,6 +357,10 @@ export default class Router implements BaseRouter {
this.notify(data)
}
/**
* Callback to execute before replacing router state
* @param cb callback to be executed
*/
beforePopState(cb: BeforePopStateCallback) {
this._bps = cb
}
......@@ -391,6 +413,11 @@ export default class Router implements BaseRouter {
return this.asPath !== asPath
}
/**
* Prefetch `page` code, you may wait for the data during `page` rendering.
* This feature only works in production!
* @param url of prefetched `page`
*/
prefetch(url: string): Promise<void> {
return new Promise((resolve, reject) => {
// Prefetch is not supported in development mode because it would trigger on-demand-entries
......
import { format, UrlObject, URLFormatOptions } from 'url'
import { ServerResponse, IncomingMessage } from 'http';
import { ServerResponse, IncomingMessage } from 'http'
import { ComponentType } from 'react'
import { ParsedUrlQuery } from 'querystring'
import { ManifestItem } from '../server/get-dynamic-import-bundles'
......@@ -8,23 +8,44 @@ import { BaseRouter } from './router/router'
/**
* Types used by both next and next-server
*/
export type NextComponentType<C extends BaseContext = NextPageContext, IP = {}, P = {}> = ComponentType<P> & {
export type NextComponentType<
C extends BaseContext = NextPageContext,
IP = {},
P = {}
> = ComponentType<P> & {
getInitialProps?(context: C): Promise<IP>,
}
export type DocumentType = NextComponentType<DocumentContext, DocumentInitialProps, DocumentProps>
export type DocumentType = NextComponentType<
DocumentContext,
DocumentInitialProps,
DocumentProps
>
export type AppType = NextComponentType<AppContextType, AppInitialProps, AppPropsType>
export type AppType = NextComponentType<
AppContextType,
AppInitialProps,
AppPropsType
>
export type Enhancer<C> = (Component: C) => C
export type ComponentsEnhancer =
| { enhanceApp?: Enhancer<AppType>; enhanceComponent?: Enhancer<NextComponentType> }
| {
enhanceApp?: Enhancer<AppType>
enhanceComponent?: Enhancer<NextComponentType>,
}
| Enhancer<NextComponentType>
export type RenderPageResult = { html: string, head?: Array<JSX.Element | null>, dataOnly?: true }
export type RenderPageResult = {
html: string
head?: Array<JSX.Element | null>
dataOnly?: true,
}
export type RenderPage = (options?: ComponentsEnhancer) => RenderPageResult | Promise<RenderPageResult>
export type RenderPage = (
options?: ComponentsEnhancer,
) => RenderPageResult | Promise<RenderPageResult>
export type BaseContext = {
res?: ServerResponse
......@@ -45,13 +66,34 @@ export type NEXT_DATA = {
err?: Error & { statusCode?: number },
}
/**
* `Next` context
*/
// tslint:disable-next-line interface-name
export interface NextPageContext {
/**
* Error object if encountered during rendering
*/
err?: Error & { statusCode?: number } | null
/**
* `HTTP` request object.
*/
req?: IncomingMessage
/**
* `HTTP` response object.
*/
res?: ServerResponse
/**
* Path section of `URL`.
*/
pathname: string
/**
* Query string section of `URL` parsed as an object.
*/
query: ParsedUrlQuery
/**
* `String` of the actual path including query.
*/
asPath?: string
}
......@@ -65,7 +107,10 @@ export type AppInitialProps = {
pageProps: any,
}
export type AppPropsType<R extends BaseRouter = BaseRouter, P = {}> = AppInitialProps & {
export type AppPropsType<
R extends BaseRouter = BaseRouter,
P = {}
> = AppInitialProps & {
Component: NextComponentType<NextPageContext, any, P>
router: R,
}
......@@ -117,17 +162,25 @@ export function getURL() {
}
export function getDisplayName(Component: ComponentType<any>) {
return typeof Component === 'string' ? Component : (Component.displayName || Component.name || 'Unknown')
return typeof Component === 'string'
? Component
: Component.displayName || Component.name || 'Unknown'
}
export function isResSent(res: ServerResponse) {
return res.finished || res.headersSent
}
export async function loadGetInitialProps<C extends BaseContext, IP = {}, P = {}>(Component: NextComponentType<C, IP, P>, ctx: C): Promise<IP | null> {
export async function loadGetInitialProps<
C extends BaseContext,
IP = {},
P = {}
>(Component: NextComponentType<C, IP, P>, ctx: C): Promise<IP | null> {
if (process.env.NODE_ENV !== 'production') {
if (Component.prototype && Component.prototype.getInitialProps) {
const message = `"${getDisplayName(Component)}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.`
const message = `"${getDisplayName(
Component,
)}.getInitialProps()" is defined as an instance method - visit https://err.sh/zeit/next.js/get-initial-props-as-an-instance-method for more information.`
throw new Error(message)
}
}
......@@ -148,28 +201,46 @@ export async function loadGetInitialProps<C extends BaseContext, IP = {}, P = {}
// set cache-control header to stale-while-revalidate
if (ctx.Component && !ctx.Component.getInitialProps) {
if (res && res.setHeader) {
res.setHeader(
'Cache-Control', 's-maxage=86400, stale-while-revalidate',
)
res.setHeader('Cache-Control', 's-maxage=86400, stale-while-revalidate')
}
}
if (!props) {
const message = `"${getDisplayName(Component)}.getInitialProps()" should resolve to an object. But found "${props}" instead.`
const message = `"${getDisplayName(
Component,
)}.getInitialProps()" should resolve to an object. But found "${props}" instead.`
throw new Error(message)
}
return props
}
export const urlObjectKeys = ['auth', 'hash', 'host', 'hostname', 'href', 'path', 'pathname', 'port', 'protocol', 'query', 'search', 'slashes']
export function formatWithValidation(url: UrlObject, options?: URLFormatOptions) {
export const urlObjectKeys = [
'auth',
'hash',
'host',
'hostname',
'href',
'path',
'pathname',
'port',
'protocol',
'query',
'search',
'slashes',
]
export function formatWithValidation(
url: UrlObject,
options?: URLFormatOptions,
) {
if (process.env.NODE_ENV === 'development') {
if (url !== null && typeof url === 'object') {
Object.keys(url).forEach((key) => {
if (urlObjectKeys.indexOf(key) === -1) {
console.warn(`Unknown key passed via urlObject into url.format: ${key}`)
console.warn(
`Unknown key passed via urlObject into url.format: ${key}`,
)
}
})
}
......
import React, {ErrorInfo} from 'react'
import React, { ErrorInfo } from 'react'
import PropTypes from 'prop-types'
import { execOnce, loadGetInitialProps, AppContextType, AppInitialProps, AppPropsType } from 'next-server/dist/lib/utils'
import { Router, makePublicRouterInstance } from '../client/router'
......@@ -9,6 +9,10 @@ export type AppContext = AppContextType<Router>
export type AppProps<P = {}> = AppPropsType<Router, P>
/**
* `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.
*/
async function appGetInitialProps({ Component, ctx }: AppContext): Promise<AppInitialProps> {
const pageProps = await loadGetInitialProps(Component, ctx)
return { pageProps }
......@@ -74,7 +78,9 @@ export class Container extends React.Component {
const warnUrl = execOnce(() => {
if (process.env.NODE_ENV !== 'production') {
console.error(`Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`)
console.error(
`Warning: the 'url' property is deprecated. https://err.sh/zeit/next.js/url-deprecated`,
)
}
})
......
......@@ -22,12 +22,20 @@ export type DocumentComponentContext = {
readonly _devOnlyInvalidateCacheQueryString: string,
}
/**
* `Document` component handles the initial `document` markup and renders only on the server side.
* Commonly used for implementing server side rendering for `css-in-js` libraries.
*/
export default class Document<P = {}> extends Component<DocumentProps & P> {
static childContextTypes = {
_documentProps: PropTypes.any,
_devOnlyInvalidateCacheQueryString: PropTypes.string,
}
/**
* `getInitialProps` hook returns the context object with the addition of `renderPage`. `
* `renderPage` callback executes `React` rendering logic synchronously to support server-rendering wrappers
*/
static async getInitialProps({ renderPage }: DocumentContext): Promise<DocumentInitialProps> {
const { html, head, dataOnly } = await renderPage()
const styles = flush()
......
......@@ -13,6 +13,9 @@ export type ErrorProps = {
statusCode: number,
}
/**
* `Error` component used for handling errors.
*/
export default class Error<P = {}> extends React.Component<P & ErrorProps> {
static displayName = 'ErrorPage'
......
......@@ -9,7 +9,7 @@ import { NextPageContext, NextComponentType } from 'next-server/dist/lib/utils';
declare module 'react' {
// <html amp=""> support
interface HtmlHTMLAttributes<T> extends React.HTMLAttributes<T> {
amp?: string;
amp?: string
}
// <link nonce=""> support
......@@ -19,14 +19,23 @@ declare module 'react' {
// <style jsx> and <style jsx global> support for styled-jsx
interface StyleHTMLAttributes<T> extends HTMLAttributes<T> {
jsx?: boolean;
global?: boolean;
jsx?: boolean
global?: boolean
}
}
/**
* `Page` type, use it as a guide to create `pages`.
*/
export type NextPage<P = {}> = {
(props: P): JSX.Element;
getInitialProps?(ctx: NextPageContext): Promise<P>;
(props: P): JSX.Element
/**
* Used for initial page load data population. Data returned from `getInitialProps` is serialized when server rendered.
* Make sure to return plain `Object` without using `Date`, `Map`, `Set`.
* @param ctx Context of `page`
*/
getInitialProps?(ctx: NextPageContext): Promise<P>
}
export { NextPageContext, NextComponentType }
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册