提交 8c19d78a 编写于 作者: J JJ Kasper 提交者: Tim Neutkens

Also provide AppTree in NextPageContext (#8223)

* Also provide AppTree in NextPageContext

* Don't predefine AppTree as null in ctx

* Make update smaller

* Don’t reassign variable

* Add test for AppTree in NextPageContext

* Update utils.ts
上级 7e1d54cc
......@@ -416,8 +416,12 @@ export default class Router implements BaseRouter {
}
return new Promise((resolve, reject) => {
const ctx = { pathname, query, asPath: as }
this.getInitialProps(Component, ctx).then(props => {
// we provide AppTree later so this needs to be `any`
this.getInitialProps(Component, {
pathname,
query,
asPath: as,
} as any).then(props => {
routeInfo.props = props
this.components[route] = routeInfo
resolve(routeInfo)
......@@ -450,9 +454,12 @@ export default class Router implements BaseRouter {
resolve(
this.fetchComponent('/_error').then(Component => {
const routeInfo: RouteInfo = { Component, err }
const ctx = { err, pathname, query }
return new Promise(resolve => {
this.getInitialProps(Component, ctx).then(
this.getInitialProps(Component, {
err,
pathname,
query,
} as any).then(
props => {
routeInfo.props = props
routeInfo.error = err
......@@ -635,8 +642,10 @@ export default class Router implements BaseRouter {
return { error: err.message, status }
})
} else {
const AppTree = this._wrapApp(App)
ctx.AppTree = AppTree
props = await loadGetInitialProps<AppContextType<Router>>(App, {
AppTree: this._wrapApp(App),
AppTree,
Component,
router: this,
ctx,
......
......@@ -96,6 +96,10 @@ export interface NextPageContext {
* `String` of the actual path including query.
*/
asPath?: string
/**
* `Component` the tree of the App to use if needing to render separately
*/
AppTree: NextComponentType
}
export type AppContextType<R extends NextRouter = NextRouter> = {
......
......@@ -303,6 +303,7 @@ export async function renderToHTML(
// @ts-ignore url will always be set
const asPath: string = req.url
const router = new ServerRouter(pathname, query, asPath)
const ctx = {
err,
req: isStaticPage ? undefined : req,
......@@ -310,12 +311,18 @@ export async function renderToHTML(
pathname,
query,
asPath,
AppTree: (props: any) => {
return (
<AppContainer>
<App {...props} Component={Component} router={router} />
</AppContainer>
)
},
}
let props: any
const isDataPrerender =
pageConfig.experimentalPrerender === true &&
req.headers['content-type'] === 'application/json'
const router = new ServerRouter(pathname, query, asPath)
let props: any
if (documentMiddlewareEnabled && typeof DocumentMiddleware === 'function') {
await DocumentMiddleware(ctx)
......
......@@ -218,11 +218,12 @@ export async function renderError (props) {
// In production we do a normal render with the `ErrorComponent` as component.
// If we've gotten here upon initial render, we can use the props from the server.
// Otherwise, we need to call `getInitialProps` on `App` before mounting.
const AppTree = wrapApp(App)
const appCtx = {
AppTree: wrapApp(App),
Component: ErrorComponent,
AppTree,
router,
ctx: { err, pathname: page, query, asPath }
ctx: { err, pathname: page, query, asPath, AppTree }
}
const initProps = props.props
......@@ -326,11 +327,12 @@ async function doRender ({ App, Component, props, err }) {
lastAppProps.Component === ErrorComponent
) {
const { pathname, query, asPath } = router
const AppTree = wrapApp(App)
const appCtx = {
router,
AppTree: wrapApp(App),
AppTree,
Component: ErrorComponent,
ctx: { err, pathname, query, asPath }
ctx: { err, pathname, query, asPath, AppTree }
}
props = await loadGetInitialProps(App, appCtx)
}
......
......@@ -39,7 +39,7 @@ class MyApp extends App {
const { Component, pageProps, html, router } = this.props
const href = router.pathname === '/' ? '/another' : '/'
return html ? (
return html && router.pathname !== '/hello' ? (
<>
<div dangerouslySetInnerHTML={{ __html: html }} />
<Link href={href}>
......@@ -48,7 +48,7 @@ class MyApp extends App {
</>
) : (
<Container>
<Component {...pageProps} {...{ html }} />
<Component {...pageProps} />
</Container>
)
}
......
import { render } from 'react-dom'
import { renderToString } from 'react-dom/server'
const Page = ({ html }) =>
html ? (
<>
<p>saved:</p>
<div dangerouslySetInnerHTML={{ __html: html }} />
</>
) : (
<p>Hello world</p>
)
Page.getInitialProps = async ({ AppTree, pathname, query, asPath }) => {
let html
const toRender = (
<AppTree router={{ pathname, query, asPath }} Component={Page} />
)
if (typeof window !== 'undefined') {
const el = document.createElement('div')
document.querySelector('body').appendChild(el)
render(toRender, el)
html = el.innerHTML
el.remove()
} else {
html = renderToString(toRender)
}
return { html }
}
export default Page
......@@ -45,6 +45,11 @@ const runTests = () => {
html = await browser.eval(`document.documentElement.innerHTML`)
expect(html).toMatch(/page:.*?\/another/)
})
it('should pass AppTree to NextPageContext', async () => {
const html = await renderViaHTTP(appPort, '/hello')
expect(html).toMatch(/saved:.*?Hello world/)
})
}
describe('Auto Export', () => {
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册