Skip to content

Commit

Permalink
rename flush effects to server inserted html (vercel#41073)
Browse files Browse the repository at this point in the history
x-ref: [slack thread](https://vercel.slack.com/archives/C035J346QQL/p1664561699861189)

Rename `useFlushEffects` to `useServerInsertedHTML`
  • Loading branch information
huozhi authored and Kikobeats committed Oct 24, 2022
1 parent 6484d1b commit 2e2542a
Show file tree
Hide file tree
Showing 8 changed files with 74 additions and 76 deletions.
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>
)
}

0 comments on commit 2e2542a

Please sign in to comment.