Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Simplify the App and Component handling in render #36395

Merged
merged 3 commits into from Apr 22, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
17 changes: 7 additions & 10 deletions packages/next/pages/_error.tsx
Expand Up @@ -41,16 +41,13 @@ export default class Error<P = {}> extends React.Component<P & ErrorProps> {

return (
<div style={styles.error}>
{/* TODO: remove this once RSC supports next/head */}
{!process.env.__NEXT_RSC && (
<Head>
<title>
{statusCode
? `${statusCode}: ${title}`
: 'Application error: a client-side exception has occurred'}
</title>
</Head>
)}
<Head>
<title>
{statusCode
? `${statusCode}: ${title}`
: 'Application error: a client-side exception has occurred'}
</title>
</Head>
<div>
<style
dangerouslySetInnerHTML={{
Expand Down
4 changes: 3 additions & 1 deletion packages/next/server/base-server.ts
Expand Up @@ -18,7 +18,7 @@ import type { PagesManifest } from '../build/webpack/plugins/pages-manifest-plug
import type { BaseNextRequest, BaseNextResponse } from './base-http'
import type { PayloadOptions } from './send-payload'

import { join, resolve } from 'path'
import pathMod from '../shared/lib/isomorphic/path'
import { parse as parseQs } from 'querystring'
import { format as formatUrl, parse as parseUrl } from 'url'
import { getRedirectStatus } from '../lib/load-custom-routes'
Expand Down Expand Up @@ -66,6 +66,8 @@ import { PrerenderManifest } from '../build'
import { ImageConfigComplete } from '../shared/lib/image-config'
import { replaceBasePath } from './router-utils'

const { join, resolve } = pathMod

export type FindComponentsResult = {
components: LoadComponentsReturnType
query: NextParsedUrlQuery
Expand Down
119 changes: 59 additions & 60 deletions packages/next/server/render.tsx
Expand Up @@ -196,20 +196,19 @@ function enhanceComponents(
}
}

function renderFlight(AppMod: any, ComponentMod: any, props: any) {
const isServerComponent = !!ComponentMod.__next_rsc__
const App = interopDefault(AppMod)
const Component = interopDefault(ComponentMod)
const AppServer = isServerComponent
? (App as React.ComponentType)
: React.Fragment
function renderPageTree(
App: any,
Component: any,
props: any,
isServerComponent: boolean
) {
const { router: _, ...rest } = props

if (isServerComponent) {
return (
<AppServer>
<App>
<Component {...rest} />
</AppServer>
</App>
)
}

Expand Down Expand Up @@ -395,6 +394,7 @@ const useFlightResponse = createFlightHook()

// Create the wrapper component for a Flight stream.
function createServerComponentRenderer(
App: any,
ComponentMod: any,
{
cachePrefix,
Expand All @@ -414,7 +414,7 @@ function createServerComponentRenderer(
globalThis.__webpack_require__ = ComponentMod.__next_rsc__.__webpack_require__
const Component = interopDefault(ComponentMod)

function ServerComponentWrapper({ App, router, ...props }: any) {
function ServerComponentWrapper({ router, ...props }: any) {
huozhi marked this conversation as resolved.
Show resolved Hide resolved
const id = (React as any).useId()

const reqStream: ReadableStream<Uint8Array> = renderToReadableStream(
Expand Down Expand Up @@ -489,7 +489,7 @@ export async function renderToHTML(
reactRoot,
runtime: globalRuntime,
ComponentMod,
AppMod: AppClientMod,
AppMod,
AppServerMod,
} = renderOpts

Expand All @@ -503,11 +503,12 @@ export async function renderToHTML(
!!serverComponentManifest &&
!!ComponentMod.__next_rsc__?.server

// Component will be wrapped by ServerComponentWrapper for RSC
let Component: React.ComponentType<{}> | ((props: any) => JSX.Element) =
renderOpts.Component
const OriginComponent = Component

const AppMod = isServerComponent ? AppServerMod : AppClientMod
const App = interopDefault(AppMod)
const App = interopDefault(isServerComponent ? AppServerMod : AppMod)

let serverComponentsInlinedTransformStream: TransformStream<
Uint8Array,
Expand All @@ -522,7 +523,7 @@ export async function renderToHTML(
if (isServerComponent) {
serverComponentsInlinedTransformStream = new TransformStream()
const search = urlQueryToSearchParams(query).toString()
Component = createServerComponentRenderer(ComponentMod, {
Component = createServerComponentRenderer(App, ComponentMod, {
cachePrefix: pathname + (search ? `?${search}` : ''),
inlinedTransformStream: serverComponentsInlinedTransformStream,
staticTransformStream: serverComponentsPageDataTransformStream,
Expand Down Expand Up @@ -743,7 +744,12 @@ export async function renderToHTML(
AppTree: (props: any) => {
return (
<AppContainerWithIsomorphicFiberStructure>
{renderFlight(AppMod, ComponentMod, { ...props, router })}
{renderPageTree(
App,
OriginComponent,
{ ...props, router },
isServerComponent
)}
</AppContainerWithIsomorphicFiberStructure>
)
},
Expand Down Expand Up @@ -1235,10 +1241,15 @@ export async function renderToHTML(
if (renderServerComponentData) {
return new RenderResult(
renderToReadableStream(
renderFlight(AppMod, ComponentMod, {
...props.pageProps,
...serverComponentProps,
}),
renderPageTree(
App,
OriginComponent,
{
...props.pageProps,
...serverComponentProps,
},
isServerComponent
),
serverComponentManifest
).pipeThrough(createBufferedTransformStream())
)
Expand Down Expand Up @@ -1323,21 +1334,18 @@ export async function renderToHTML(
}

async function documentInitialProps(
renderShell?: ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
}) => Promise<void>
renderShell?: (
_App: AppType,
_Component: NextComponentType
) => Promise<void>
) {
const renderPage: RenderPage = (
options: ComponentsEnhancer = {}
): RenderPageResult | Promise<RenderPageResult> => {
if (ctx.err && ErrorDebug) {
// Always start rendering the shell even if there's an error.
if (renderShell) {
renderShell({})
renderShell(App, Component)
}

const html = ReactDOMServer.renderToString(
Expand All @@ -1358,7 +1366,7 @@ export async function renderToHTML(
enhanceComponents(options, App, Component)

if (renderShell) {
return renderShell({ EnhancedApp, EnhancedComponent }).then(() => {
return renderShell(EnhancedApp, EnhancedComponent).then(() => {
// When using concurrent features, we don't have or need the full
// html so it's fine to return nothing here.
return { html: '', head }
Expand All @@ -1368,11 +1376,12 @@ export async function renderToHTML(
const html = ReactDOMServer.renderToString(
<Body>
<AppContainerWithIsomorphicFiberStructure>
<EnhancedApp
Component={EnhancedComponent}
router={router}
{...props}
/>
{renderPageTree(
EnhancedApp,
EnhancedComponent,
{ ...props, router },
false
)}
</AppContainerWithIsomorphicFiberStructure>
</Body>
)
Expand All @@ -1396,30 +1405,23 @@ export async function renderToHTML(
return { docProps, documentCtx }
}

const renderContent = ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
} = {}) => {
const renderContent = (_App: AppType, _Component: NextComponentType) => {
const EnhancedApp = _App || App
const EnhancedComponent = _Component || Component

return ctx.err && ErrorDebug ? (
<Body>
<ErrorDebug error={ctx.err} />
</Body>
) : (
<Body>
<AppContainerWithIsomorphicFiberStructure>
{isServerComponent
? React.createElement(EnhancedComponent || Component, {
App: EnhancedApp || App,
...props.pageProps,
})
: React.createElement(EnhancedApp || App, {
...props,
Component: EnhancedComponent || Component,
router,
})}
{renderPageTree(
EnhancedApp,
EnhancedComponent,
{ ...(isServerComponent ? props.pageProps : props), router },
isServerComponent
)}
</AppContainerWithIsomorphicFiberStructure>
</Body>
)
Expand All @@ -1442,7 +1444,7 @@ export async function renderToHTML(
styles: docProps.styles,
}
} else {
const content = renderContent()
const content = renderContent(App, Component)
// for non-concurrent rendering we need to ensure App is rendered
// before _document so that updateHead is called/collected before
// rendering _document's head
Expand All @@ -1465,14 +1467,11 @@ export async function renderToHTML(
allReady?: Promise<void> | undefined
}

const renderShell = async ({
EnhancedApp,
EnhancedComponent,
}: {
EnhancedApp?: AppType
EnhancedComponent?: NextComponentType
} = {}) => {
const content = renderContent({ EnhancedApp, EnhancedComponent })
const renderShell = async (
EnhancedApp: AppType,
EnhancedComponent: NextComponentType
) => {
const content = renderContent(EnhancedApp, EnhancedComponent)
renderStream = await renderToInitialStream({
ReactDOMServer,
element: content,
Expand Down Expand Up @@ -1555,7 +1554,7 @@ export async function renderToHTML(
documentInitialPropsRes = await documentInitialProps(renderShell)
if (documentInitialPropsRes === null) return null
} else {
await renderShell()
await renderShell(App, Component)
documentInitialPropsRes = {}
}

Expand Down