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

rename flush effects to server inserted html #41073

Merged
merged 3 commits into from Oct 3, 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
2 changes: 1 addition & 1 deletion packages/next/build/webpack-config.ts
Expand Up @@ -87,7 +87,7 @@ const BABEL_CONFIG_FILES = [
]

const rscSharedRegex =
/(node_modules[\\/]react\/|[\\/]shared[\\/]lib[\\/](head-manager-context|router-context|flush-effects)\.js|node_modules[\\/]styled-jsx[\\/])/
/(node_modules[\\/]react\/|[\\/]shared[\\/]lib[\\/](head-manager-context|router-context|server-inserted-html)\.js|node_modules[\\/]styled-jsx[\\/])/

// Support for NODE_PATH
const nodePathList = (process.env.NODE_PATH || '')
Expand Down
6 changes: 3 additions & 3 deletions packages/next/client/components/hooks-client.ts
Expand Up @@ -13,9 +13,9 @@ import {
} from '../../shared/lib/app-router-context'

export {
FlushEffectsContext,
useFlushEffects,
} from '../../shared/lib/flush-effects'
ServerInsertedHTMLContext,
useServerInsertedHTML,
} from '../../shared/lib/server-inserted-html'

/**
* Get the current search params. For example useSearchParams() would return {"foo": "bar"} when ?foo=bar
Expand Down
38 changes: 21 additions & 17 deletions packages/next/server/app-render.tsx
Expand Up @@ -26,7 +26,7 @@ import {
FlightCSSManifest,
FlightManifest,
} from '../build/webpack/plugins/flight-manifest-plugin'
import { FlushEffectsContext } from '../shared/lib/flush-effects'
import { ServerInsertedHTMLContext } from '../shared/lib/server-inserted-html'
import { stripInternalQueries } from './internal-utils'
import type { ComponentsType } from '../build/webpack/loaders/next-app-loader'
import { REDIRECT_ERROR_CODE } from '../client/components/redirect'
Expand Down Expand Up @@ -1301,34 +1301,38 @@ export async function renderToHTMLOrFlight(
nonce
)

const flushEffectsCallbacks: Set<() => React.ReactNode> = new Set()
function FlushEffects({ children }: { children: JSX.Element }) {
// Reset flushEffectsHandler on each render
flushEffectsCallbacks.clear()
const addFlushEffects = React.useCallback(
const serverInsertedHTMLCallbacks: Set<() => React.ReactNode> = new Set()
function InsertedHTML({ children }: { children: JSX.Element }) {
// Reset addInsertedHtmlCallback on each render
serverInsertedHTMLCallbacks.clear()
const addInsertedHtml = React.useCallback(
(handler: () => React.ReactNode) => {
flushEffectsCallbacks.add(handler)
serverInsertedHTMLCallbacks.add(handler)
},
[]
)

return (
<FlushEffectsContext.Provider value={addFlushEffects}>
<ServerInsertedHTMLContext.Provider value={addInsertedHtml}>
{children}
</FlushEffectsContext.Provider>
</ServerInsertedHTMLContext.Provider>
)
}

const bodyResult = async () => {
const content = (
<FlushEffects>
<InsertedHTML>
<ServerComponentsRenderer />
</FlushEffects>
</InsertedHTML>
)

const flushEffectHandler = (): Promise<string> => {
const getServerInsertedHTML = (): Promise<string> => {
const flushed = renderToString(
<>{Array.from(flushEffectsCallbacks).map((callback) => callback())}</>
<>
{Array.from(serverInsertedHTMLCallbacks).map((callback) =>
callback()
)}
</>
)
return flushed
}
Expand Down Expand Up @@ -1367,8 +1371,8 @@ export async function renderToHTMLOrFlight(
return await continueFromInitialStream(renderStream, {
dataStream: serverComponentsInlinedTransformStream?.readable,
generateStaticHTML: generateStaticHTML,
flushEffectHandler,
flushEffectsToHead: true,
getServerInsertedHTML,
serverInsertedHTMLToHead: true,
polyfills,
})
} catch (err: any) {
Expand Down Expand Up @@ -1398,8 +1402,8 @@ export async function renderToHTMLOrFlight(
return await continueFromInitialStream(renderStream, {
dataStream: serverComponentsInlinedTransformStream?.readable,
generateStaticHTML: generateStaticHTML,
flushEffectHandler,
flushEffectsToHead: true,
getServerInsertedHTML,
serverInsertedHTMLToHead: true,
polyfills,
})
}
Expand Down
30 changes: 15 additions & 15 deletions packages/next/server/node-web-streams-helper.ts
Expand Up @@ -123,14 +123,14 @@ export function createBufferedTransformStream(
})
}

export function createFlushEffectStream(
handleFlushEffect: () => Promise<string>
export function createInsertedHTMLStream(
getServerInsertedHTML: () => Promise<string>
): TransformStream<Uint8Array, Uint8Array> {
return new TransformStream({
async transform(chunk, controller) {
const flushedChunk = encodeText(await handleFlushEffect())
const insertedHTMLChunk = encodeText(await getServerInsertedHTML())

controller.enqueue(flushedChunk)
controller.enqueue(insertedHTMLChunk)
controller.enqueue(chunk)
},
})
Expand Down Expand Up @@ -263,15 +263,15 @@ export async function continueFromInitialStream(
suffix,
dataStream,
generateStaticHTML,
flushEffectHandler,
flushEffectsToHead,
getServerInsertedHTML,
serverInsertedHTMLToHead,
polyfills,
}: {
suffix?: string
dataStream?: ReadableStream<Uint8Array>
generateStaticHTML: boolean
flushEffectHandler?: () => Promise<string>
flushEffectsToHead: boolean
getServerInsertedHTML?: () => Promise<string>
serverInsertedHTMLToHead: boolean
polyfills?: { src: string; integrity: string | undefined }[]
}
): Promise<ReadableStream<Uint8Array>> {
Expand All @@ -284,8 +284,8 @@ export async function continueFromInitialStream(

const transforms: Array<TransformStream<Uint8Array, Uint8Array>> = [
createBufferedTransformStream(),
flushEffectHandler && !flushEffectsToHead
? createFlushEffectStream(flushEffectHandler)
getServerInsertedHTML && !serverInsertedHTMLToHead
? createInsertedHTMLStream(getServerInsertedHTML)
: null,
suffixUnclosed != null ? createDeferredSuffixStream(suffixUnclosed) : null,
dataStream ? createInlineDataStream(dataStream) : null,
Expand All @@ -304,13 +304,13 @@ export async function continueFromInitialStream(
.join('')
: ''

// TODO-APP: Inject flush effects to end of head in app layout rendering, to avoid
// TODO-APP: Insert server side html to end of head in app layout rendering, to avoid
// hydration errors. Remove this once it's ready to be handled by react itself.
const flushEffectsContent =
flushEffectHandler && flushEffectsToHead
? await flushEffectHandler()
const serverInsertedHTML =
getServerInsertedHTML && serverInsertedHTMLToHead
? await getServerInsertedHTML()
: ''
return polyfillScripts + flushEffectsContent
return polyfillScripts + serverInsertedHTML
}),
].filter(nonNullable)

Expand Down
10 changes: 5 additions & 5 deletions packages/next/server/render.tsx
Expand Up @@ -735,7 +735,7 @@ export async function renderToHTML(
const nextExport =
!isSSG && (renderOpts.nextExport || (dev && (isAutoExport || isFallback)))

const styledJsxFlushEffect = () => {
const styledJsxInsertedHTML = () => {
const styles = jsxStyleRegistry.styles()
jsxStyleRegistry.flush()
return <>{styles}</>
Expand Down Expand Up @@ -1301,16 +1301,16 @@ export async function renderToHTML(
) => {
// this must be called inside bodyResult so appWrappers is
// up to date when `wrapApp` is called
const flushEffectHandler = async (): Promise<string> => {
return renderToString(styledJsxFlushEffect())
const getServerInsertedHTML = async (): Promise<string> => {
return renderToString(styledJsxInsertedHTML())
}

return continueFromInitialStream(initialStream, {
suffix,
dataStream: serverComponentsInlinedTransformStream?.readable,
generateStaticHTML,
flushEffectHandler,
flushEffectsToHead: false,
getServerInsertedHTML,
serverInsertedHTMLToHead: false,
})
}

Expand Down
21 changes: 0 additions & 21 deletions packages/next/shared/lib/flush-effects.tsx

This file was deleted.

20 changes: 20 additions & 0 deletions packages/next/shared/lib/server-inserted-html.tsx
@@ -0,0 +1,20 @@
import React, { useContext } from 'react'

export type ServerInsertedHTMLHook = (callbacks: () => React.ReactNode) => void

// Use `React.createContext` to avoid errors from the RSC checks because
// it can't be imported directly in Server Components:
//
// import { createContext } from 'react'
//
// More info: https://github.com/vercel/next.js/pull/40686
export const ServerInsertedHTMLContext =
React.createContext<ServerInsertedHTMLHook | null>(null as any)

export function useServerInsertedHTML(callback: () => React.ReactNode): void {
const addInsertedServerHTMLCallback = useContext(ServerInsertedHTMLContext)
// Should have no effects on client where there's no flush effects provider
if (addInsertedServerHTMLCallback) {
addInsertedServerHTMLCallback(callback)
}
}
23 changes: 9 additions & 14 deletions test/e2e/app-dir/rsc-basic/app/root-style-registry.js
Expand Up @@ -3,7 +3,7 @@
import React from 'react'
import { StyleRegistry, createStyleRegistry } from 'styled-jsx'
import { ServerStyleSheet, StyleSheetManager } from 'styled-components'
import { useFlushEffects } from 'next/dist/client/components/hooks-client'
import { useServerInsertedHTML } from 'next/dist/client/components/hooks-client'
import { useState } from 'react'

export default function RootStyleRegistry({ children }) {
Expand All @@ -21,23 +21,18 @@ export default function RootStyleRegistry({ children }) {
return <>{styles}</>
}

// Allow multiple useFlushEffects
useFlushEffects(() => {
// Allow multiple useServerInsertedHTML
useServerInsertedHTML(() => {
return <>{styledJsxFlushEffect()}</>
})

useFlushEffects(() => {
useServerInsertedHTML(() => {
return <>{styledComponentsFlushEffect()}</>
})

// Only include style registry on server side for SSR
if (typeof window === 'undefined') {
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
<StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
</StyleSheetManager>
)
}

return children
return (
<StyleSheetManager sheet={styledComponentsStyleSheet.instance}>
<StyleRegistry registry={jsxStyleRegistry}>{children}</StyleRegistry>
</StyleSheetManager>
)
}