From 2e2542a9953d42df6945cab3213f8751010b8950 Mon Sep 17 00:00:00 2001 From: Jiachi Liu Date: Mon, 3 Oct 2022 15:43:35 +0200 Subject: [PATCH] rename flush effects to server inserted html (#41073) x-ref: [slack thread](https://vercel.slack.com/archives/C035J346QQL/p1664561699861189) Rename `useFlushEffects` to `useServerInsertedHTML` --- packages/next/build/webpack-config.ts | 2 +- .../next/client/components/hooks-client.ts | 6 +-- packages/next/server/app-render.tsx | 38 ++++++++++--------- .../next/server/node-web-streams-helper.ts | 30 +++++++-------- packages/next/server/render.tsx | 10 ++--- packages/next/shared/lib/flush-effects.tsx | 21 ---------- .../next/shared/lib/server-inserted-html.tsx | 20 ++++++++++ .../rsc-basic/app/root-style-registry.js | 23 +++++------ 8 files changed, 74 insertions(+), 76 deletions(-) delete mode 100644 packages/next/shared/lib/flush-effects.tsx create mode 100644 packages/next/shared/lib/server-inserted-html.tsx diff --git a/packages/next/build/webpack-config.ts b/packages/next/build/webpack-config.ts index 4e3d4ea27f58b..a875d60638249 100644 --- a/packages/next/build/webpack-config.ts +++ b/packages/next/build/webpack-config.ts @@ -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 || '') diff --git a/packages/next/client/components/hooks-client.ts b/packages/next/client/components/hooks-client.ts index 09a834ac14584..eb7a06f39ec05 100644 --- a/packages/next/client/components/hooks-client.ts +++ b/packages/next/client/components/hooks-client.ts @@ -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 diff --git a/packages/next/server/app-render.tsx b/packages/next/server/app-render.tsx index 3fb3e580a2e4d..ae57f0fb60e15 100644 --- a/packages/next/server/app-render.tsx +++ b/packages/next/server/app-render.tsx @@ -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' @@ -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 ( - + {children} - + ) } const bodyResult = async () => { const content = ( - + - + ) - const flushEffectHandler = (): Promise => { + const getServerInsertedHTML = (): Promise => { const flushed = renderToString( - <>{Array.from(flushEffectsCallbacks).map((callback) => callback())} + <> + {Array.from(serverInsertedHTMLCallbacks).map((callback) => + callback() + )} + ) return flushed } @@ -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) { @@ -1398,8 +1402,8 @@ export async function renderToHTMLOrFlight( return await continueFromInitialStream(renderStream, { dataStream: serverComponentsInlinedTransformStream?.readable, generateStaticHTML: generateStaticHTML, - flushEffectHandler, - flushEffectsToHead: true, + getServerInsertedHTML, + serverInsertedHTMLToHead: true, polyfills, }) } diff --git a/packages/next/server/node-web-streams-helper.ts b/packages/next/server/node-web-streams-helper.ts index 8297c558ed077..a3ec622baa19d 100644 --- a/packages/next/server/node-web-streams-helper.ts +++ b/packages/next/server/node-web-streams-helper.ts @@ -123,14 +123,14 @@ export function createBufferedTransformStream( }) } -export function createFlushEffectStream( - handleFlushEffect: () => Promise +export function createInsertedHTMLStream( + getServerInsertedHTML: () => Promise ): TransformStream { 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) }, }) @@ -263,15 +263,15 @@ export async function continueFromInitialStream( suffix, dataStream, generateStaticHTML, - flushEffectHandler, - flushEffectsToHead, + getServerInsertedHTML, + serverInsertedHTMLToHead, polyfills, }: { suffix?: string dataStream?: ReadableStream generateStaticHTML: boolean - flushEffectHandler?: () => Promise - flushEffectsToHead: boolean + getServerInsertedHTML?: () => Promise + serverInsertedHTMLToHead: boolean polyfills?: { src: string; integrity: string | undefined }[] } ): Promise> { @@ -284,8 +284,8 @@ export async function continueFromInitialStream( const transforms: Array> = [ createBufferedTransformStream(), - flushEffectHandler && !flushEffectsToHead - ? createFlushEffectStream(flushEffectHandler) + getServerInsertedHTML && !serverInsertedHTMLToHead + ? createInsertedHTMLStream(getServerInsertedHTML) : null, suffixUnclosed != null ? createDeferredSuffixStream(suffixUnclosed) : null, dataStream ? createInlineDataStream(dataStream) : null, @@ -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) diff --git a/packages/next/server/render.tsx b/packages/next/server/render.tsx index 2540c3274ab29..d6454a76b36b5 100644 --- a/packages/next/server/render.tsx +++ b/packages/next/server/render.tsx @@ -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} @@ -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 => { - return renderToString(styledJsxFlushEffect()) + const getServerInsertedHTML = async (): Promise => { + return renderToString(styledJsxInsertedHTML()) } return continueFromInitialStream(initialStream, { suffix, dataStream: serverComponentsInlinedTransformStream?.readable, generateStaticHTML, - flushEffectHandler, - flushEffectsToHead: false, + getServerInsertedHTML, + serverInsertedHTMLToHead: false, }) } diff --git a/packages/next/shared/lib/flush-effects.tsx b/packages/next/shared/lib/flush-effects.tsx deleted file mode 100644 index 7d9a0c1ae42d0..0000000000000 --- a/packages/next/shared/lib/flush-effects.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import React, { useContext } from 'react' - -export type FlushEffectsHook = (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 FlushEffectsContext = React.createContext( - null as any -) - -export function useFlushEffects(callback: () => React.ReactNode): void { - const addFlushEffects = useContext(FlushEffectsContext) - // Should have no effects on client where there's no flush effects provider - if (addFlushEffects) { - addFlushEffects(callback) - } -} diff --git a/packages/next/shared/lib/server-inserted-html.tsx b/packages/next/shared/lib/server-inserted-html.tsx new file mode 100644 index 0000000000000..1bb4e1fc735a8 --- /dev/null +++ b/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(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) + } +} diff --git a/test/e2e/app-dir/rsc-basic/app/root-style-registry.js b/test/e2e/app-dir/rsc-basic/app/root-style-registry.js index 02fba3e8ea58f..2590d0434a6af 100644 --- a/test/e2e/app-dir/rsc-basic/app/root-style-registry.js +++ b/test/e2e/app-dir/rsc-basic/app/root-style-registry.js @@ -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 }) { @@ -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 ( - - {children} - - ) - } - - return children + return ( + + {children} + + ) }